diff --git a/CMakeLists.txt b/CMakeLists.txt index a331ca1c..b0b1032d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -228,6 +228,7 @@ set(SOURCE_FILES MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.cpp MobileGL/MG_State/GLState/Core.cpp + MobileGL/MG_State/EGLState/Core.cpp MobileGL/MG_State/GLState/ErrorState/Error.cpp MobileGL/MG_State/GLState/BufferState/BufferState.cpp MobileGL/MG_State/GLState/BufferState/BufferObject.cpp diff --git a/MobileGL/Init.cpp b/MobileGL/Init.cpp index 0a5069c7..74d458ae 100644 --- a/MobileGL/Init.cpp +++ b/MobileGL/Init.cpp @@ -10,6 +10,7 @@ #include "Config.h" #include #include +#include #include #include @@ -34,6 +35,7 @@ namespace MobileGL { MGLOG_I("MobileGL closing..."); glslang::FinalizeProcess(); delete MG_State::pGLContext; + delete MG_State::pEGLContext; delete MG_Impl::GLImpl::TextureImpl::pProxyTextureManager; delete MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo; MG_Util::Debug::Close(); diff --git a/MobileGL/MG_Backend/BackendObject.cpp b/MobileGL/MG_Backend/BackendObject.cpp index 87869987..d8b93030 100644 --- a/MobileGL/MG_Backend/BackendObject.cpp +++ b/MobileGL/MG_Backend/BackendObject.cpp @@ -9,7 +9,133 @@ #include "BackendObject.h" namespace MobileGL::MG_Backend { + 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; + } + + std::thread::id CurrentThreadKey() { + return std::this_thread::get_id(); + } + } // namespace + + Bool BackendObject::InitializeEGLDisplay(EGLDisplay dpy, EGLint* major, EGLint* minor) { + const std::lock_guard lock(m_eglStateMutex); + if (dpy == EGL_NO_DISPLAY) { + MGLOG_E("InitializeEGLDisplay failed: invalid EGLDisplay"); + return false; + } + + if (m_eglDisplayInitialized && m_eglDisplay != dpy) { + MGLOG_E("InitializeEGLDisplay failed: backend already bound to a different EGLDisplay"); + return false; + } + + m_eglDisplay = dpy; + m_eglDisplayInitialized = true; + if (major) { + *major = 1; + } + if (minor) { + *minor = 5; + } + return true; + } + + Bool BackendObject::CreateEGLWindowSurface(const WindowHandle& handle) { + const std::lock_guard lock(m_eglStateMutex); + if (!m_eglDisplayInitialized) { + MGLOG_E("CreateEGLWindowSurface failed: EGL display is not initialized"); + return false; + } + if (handle.Backend == WindowBackend::Unknown || !handle.Handle) { + MGLOG_E("CreateEGLWindowSurface failed: invalid native window handle"); + return false; + } + + if (m_eglWindowSurfaceInitialized && m_windowHandle.Backend == handle.Backend && m_windowHandle.Handle == handle.Handle) { + return true; + } + + SetWindowHandle(handle); + if (!InitWindowSurface()) { + MGLOG_E("CreateEGLWindowSurface failed: backend InitWindowSurface failed"); + return false; + } + + m_eglWindowSurfaceInitialized = true; + m_eglCurrentThreads.clear(); + m_backendCapabilitiesInitialized = false; + return true; + } + + Bool BackendObject::MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) { + const std::lock_guard lock(m_eglStateMutex); + const auto threadKey = CurrentThreadKey(); + if (IsReleaseCurrentRequest(dpy, draw, read, ctx)) { + m_eglCurrentThreads.erase(threadKey); + return true; + } + + if (!m_eglDisplayInitialized || m_eglDisplay != dpy) { + MGLOG_E("MakeEGLCurrent failed: EGL display mismatch or not initialized"); + return false; + } + if (!m_eglWindowSurfaceInitialized) { + MGLOG_E("MakeEGLCurrent failed: EGL window 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 (!m_backendCapabilitiesInitialized) { + if (!InitCapabilities()) { + MGLOG_E("MakeEGLCurrent failed: InitCapabilities failed"); + return false; + } + m_backendCapabilitiesInitialized = true; + } + + m_eglCurrentThreads[threadKey] = true; + return true; + } + + void BackendObject::ResetEGLRuntimeState() { + const std::lock_guard lock(m_eglStateMutex); + m_eglWindowSurfaceInitialized = false; + m_backendCapabilitiesInitialized = false; + m_eglCurrentThreads.clear(); + } + + Bool BackendObject::SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw) { + const std::lock_guard lock(m_eglStateMutex); + if (!m_eglDisplayInitialized || m_eglDisplay != dpy) { + MGLOG_E("SwapEGLBuffers failed: EGL display mismatch or not initialized"); + return false; + } + if (m_eglCurrentThreads.find(CurrentThreadKey()) == m_eglCurrentThreads.end()) { + MGLOG_E("SwapEGLBuffers failed: no current context attached"); + return false; + } + if (!m_eglWindowSurfaceInitialized || draw == EGL_NO_SURFACE) { + MGLOG_E("SwapEGLBuffers failed: invalid draw surface"); + return false; + } + + const auto& backendFunctions = GetBackendFunctions(); + if (!backendFunctions.Present) { + MGLOG_E("SwapEGLBuffers failed: backend Present function is null"); + return false; + } + + backendFunctions.Present(); + return true; + } + void BackendObject::SetWindowHandle(const WindowHandle& handle) { + const std::lock_guard lock(m_eglStateMutex); m_windowHandle = handle; } } // namespace MobileGL::MG_Backend diff --git a/MobileGL/MG_Backend/BackendObject.h b/MobileGL/MG_Backend/BackendObject.h index 7befafa7..03904df2 100644 --- a/MobileGL/MG_Backend/BackendObject.h +++ b/MobileGL/MG_Backend/BackendObject.h @@ -91,8 +91,13 @@ namespace MobileGL { virtual ~BackendObject() = default; virtual void Initialize() = 0; - virtual void InitCapabilities() = 0; - virtual void InitWindowSurface() = 0; + virtual Bool InitCapabilities() = 0; + virtual Bool InitWindowSurface() = 0; + + virtual Bool InitializeEGLDisplay(EGLDisplay dpy, EGLint* major, EGLint* minor); + virtual Bool CreateEGLWindowSurface(const WindowHandle& handle); + virtual Bool MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx); + virtual Bool SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw); void SetWindowHandle(const WindowHandle& handle); @@ -103,7 +108,15 @@ namespace MobileGL { virtual BackendType GetBackendType() const = 0; protected: + void ResetEGLRuntimeState(); + + mutable std::recursive_mutex m_eglStateMutex; WindowHandle m_windowHandle; + EGLDisplay m_eglDisplay = EGL_NO_DISPLAY; + Bool m_eglDisplayInitialized = false; + Bool m_eglWindowSurfaceInitialized = false; + Bool m_backendCapabilitiesInitialized = false; + 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 81a63e40..577e16f2 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp @@ -13,16 +13,24 @@ #include 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; + } + } // namespace + BackendObject_DirectGLES::~BackendObject_DirectGLES() { DestroyEGLContext(); } - void BackendObject_DirectGLES::InitWindowSurface() { + Bool BackendObject_DirectGLES::InitWindowSurface() { // Only use EGL for now auto nativeWindow = reinterpret_cast(m_windowHandle.Handle); if (!DirectGLES::InitWindowSurface(nativeWindow)) { MGLOG_E("Failed to initialize window surface for DirectGLES backend"); + return false; } + return true; } void BackendObject_DirectGLES::Initialize() { @@ -40,18 +48,65 @@ namespace MobileGL::MG_Backend::DirectGLES { DirectGLES::SetGLESFuncsTable(m_GLESFunctions); } - void BackendObject_DirectGLES::InitCapabilities() { + Bool BackendObject_DirectGLES::InitCapabilities() { if (!m_initialized) { MGLOG_E("DirectGLES backend not initialized"); - return; + return false; } if (!MG_Util::BackendLoader::FillInGLESCapabilities(m_GLESCapabilities, m_GLESFunctions)) { MGLOG_E("Failed to fill in GLES capabilities for DirectGLES backend"); - return; + return false; } DirectGLES::SetGLESCapabilities(m_GLESCapabilities); UpdateDynamicBackendParameters(); + return true; + } + + Bool BackendObject_DirectGLES::InitializeEGLDisplay(EGLDisplay dpy, EGLint* major, EGLint* minor) { + if (!m_initialized) { + MGLOG_E("DirectGLES backend not initialized"); + return false; + } + return BackendObject::InitializeEGLDisplay(dpy, major, minor); + } + + Bool BackendObject_DirectGLES::CreateEGLWindowSurface(const WindowHandle& handle) { + const std::lock_guard lock(m_eglStateMutex); + if (!m_initialized) { + MGLOG_E("DirectGLES backend not initialized"); + return false; + } + + if (handle.Backend != WindowBackend::Android || !handle.Handle) { + MGLOG_E("DirectGLES backend only supports Android native windows"); + return false; + } + + const Bool sameHandle = + m_eglWindowSurfaceInitialized && m_windowHandle.Backend == handle.Backend && m_windowHandle.Handle == handle.Handle; + if (sameHandle) { + return true; + } + + if (m_eglWindowSurfaceInitialized) { + DestroyEGLContext(); + ResetEGLRuntimeState(); + } + + return BackendObject::CreateEGLWindowSurface(handle); + } + + Bool BackendObject_DirectGLES::MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) { + if (IsReleaseCurrentRequest(dpy, draw, read, ctx)) { + return BackendObject::MakeEGLCurrent(dpy, draw, read, ctx); + } + + return BackendObject::MakeEGLCurrent(dpy, draw, read, ctx); + } + + Bool BackendObject_DirectGLES::SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw) { + return BackendObject::SwapEGLBuffers(dpy, draw); } const RendererInfo& BackendObject_DirectGLES::GetRendererInfo() const { diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h index d85b9623..84b0bc49 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h @@ -17,8 +17,12 @@ namespace MobileGL::MG_Backend::DirectGLES { ~BackendObject_DirectGLES() override; void Initialize() override; - void InitCapabilities() override; - void InitWindowSurface() override; + Bool InitCapabilities() override; + Bool InitWindowSurface() override; + Bool InitializeEGLDisplay(EGLDisplay dpy, EGLint* major, EGLint* minor) override; + Bool CreateEGLWindowSurface(const WindowHandle& handle) override; + Bool MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) override; + Bool SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw) override; const RendererInfo& GetRendererInfo() const override; String GetBackendAPIVersionString() const override; diff --git a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp index cf89670e..ca7285bd 100644 --- a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp @@ -11,27 +11,98 @@ #include "DirectVulkan.h" 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; + } + } // namespace + BackendObject_DirectVulkan::~BackendObject_DirectVulkan() = default; - void BackendObject_DirectVulkan::InitWindowSurface() { + Bool BackendObject_DirectVulkan::InitWindowSurface() { + if (!m_windowHandle.Handle) { + MGLOG_E("Cannot initialize DirectVulkan window surface: native window handle is null"); + return false; + } + auto nativeWindow = reinterpret_cast(m_windowHandle.Handle); pVulkanRenderer = MakeUnique(nativeWindow); pVulkanRenderer->Initialize(); + return true; } void BackendObject_DirectVulkan::Initialize() { m_initialized = true; } - void BackendObject_DirectVulkan::InitCapabilities() { + Bool BackendObject_DirectVulkan::InitCapabilities() { if (!m_initialized) { MGLOG_E("Cannot initialize capabilities before backend is initialized"); - return; + return false; + } + if (!pVulkanRenderer) { + MGLOG_E("Cannot initialize capabilities: Vulkan renderer has not been created"); + return false; } MG_Util::BackendLoader::FillInVulkanCapabilities(m_vulkanCaps, pVulkanRenderer->GetPhysicalDevice().properties); UpdateDynamicBackendParameters(); + return true; + } + + Bool BackendObject_DirectVulkan::InitializeEGLDisplay(EGLDisplay dpy, EGLint* major, EGLint* minor) { + if (!m_initialized) { + MGLOG_E("DirectVulkan backend not initialized"); + return false; + } + return BackendObject::InitializeEGLDisplay(dpy, major, minor); + } + + Bool BackendObject_DirectVulkan::CreateEGLWindowSurface(const WindowHandle& handle) { + const std::lock_guard lock(m_eglStateMutex); + if (!m_initialized) { + MGLOG_E("DirectVulkan backend not initialized"); + return false; + } + if (handle.Backend != WindowBackend::Android || !handle.Handle) { + MGLOG_E("DirectVulkan backend only supports Android native windows"); + return false; + } + + const Bool sameHandle = + m_eglWindowSurfaceInitialized && m_windowHandle.Backend == handle.Backend && m_windowHandle.Handle == handle.Handle; + if (sameHandle) { + return true; + } + + if (m_eglWindowSurfaceInitialized || pVulkanRenderer) { + pVulkanRenderer.reset(); + ResetEGLRuntimeState(); + } + + return BackendObject::CreateEGLWindowSurface(handle); + } + + Bool BackendObject_DirectVulkan::MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) { + const std::lock_guard lock(m_eglStateMutex); + if (IsReleaseCurrentRequest(dpy, draw, read, ctx)) { + return BackendObject::MakeEGLCurrent(dpy, draw, read, ctx); + } + if (!pVulkanRenderer) { + MGLOG_E("DirectVulkan renderer is not initialized"); + return false; + } + return BackendObject::MakeEGLCurrent(dpy, draw, read, ctx); + } + + Bool BackendObject_DirectVulkan::SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw) { + const std::lock_guard lock(m_eglStateMutex); + if (!pVulkanRenderer) { + MGLOG_E("DirectVulkan renderer is not initialized"); + return false; + } + return BackendObject::SwapEGLBuffers(dpy, draw); } const RendererInfo& BackendObject_DirectVulkan::GetRendererInfo() const { diff --git a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h index 44d8107d..0379c50b 100644 --- a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h +++ b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h @@ -17,8 +17,12 @@ namespace MobileGL::MG_Backend::DirectVulkan { ~BackendObject_DirectVulkan() override; void Initialize() override; - void InitWindowSurface() override; - void InitCapabilities() override; + Bool InitWindowSurface() override; + Bool InitCapabilities() override; + Bool InitializeEGLDisplay(EGLDisplay dpy, EGLint* major, EGLint* minor) override; + Bool CreateEGLWindowSurface(const WindowHandle& handle) override; + Bool MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) override; + Bool SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw) override; const RendererInfo& GetRendererInfo() const override; String GetBackendAPIVersionString() const override; diff --git a/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp b/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp index d5f4da55..d54dad22 100644 --- a/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp +++ b/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp @@ -9,177 +9,451 @@ #include "EGLImpl.h" #include "../GetProcAddress.h" #include +#include +#include namespace MobileGL::MG_Impl::EGLImpl { + namespace { + using EGLStateContext = MG_State::EGLState::EGLContext; + + EGLStateContext* GetState() { + if (!MG_State::pEGLContext) { + MGLOG_E("pEGLContext is null. MG_State may not be initialized."); + } + return MG_State::pEGLContext; + } + + MG_Backend::BackendObject* GetBackendObject(EGLStateContext* state) { + auto* backendObject = MG_Backend::pActiveBackendObject.get(); + if (!backendObject && state) { + state->SetError(EGL_NOT_INITIALIZED); + } + return backendObject; + } + + MG_Backend::WindowBackend DetectWindowBackend() { +#if defined(ANDROID) || defined(__ANDROID__) + return MG_Backend::WindowBackend::Android; +#else + return MG_Backend::WindowBackend::Unknown; +#endif + } + + template + Bool IsNullNativeHandle(NativeType nativeHandle) { + if constexpr (std::is_pointer_v) { + return nativeHandle == nullptr; + } else { + return nativeHandle == 0; + } + } + + template + void* ToVoidHandle(NativeType nativeHandle) { + if constexpr (std::is_pointer_v) { + return reinterpret_cast(nativeHandle); + } else { + return reinterpret_cast(static_cast(nativeHandle)); + } + } + } // namespace + EGLSurface CreateWindowSurface(EGLDisplay dpy, EGLConfig config, NativeWindowType window, const EGLint* attrib_list) { - MGLOG_D("EGLImpl::CreateWindowSurface called with window=%p", window); - const auto& activeBackendObject = MG_Backend::pActiveBackendObject; - if (!activeBackendObject) { + auto* state = GetState(); + if (!state) { + return EGL_NO_SURFACE; + } + if (!state->IsDisplayInitialized(dpy)) { + state->SetError(EGL_NOT_INITIALIZED); + return EGL_NO_SURFACE; + } + if (!state->ValidateConfigOnDisplay(dpy, config)) { + state->SetError(EGL_BAD_CONFIG); + return EGL_NO_SURFACE; + } + if (IsNullNativeHandle(window)) { + state->SetError(EGL_BAD_NATIVE_WINDOW); + return EGL_NO_SURFACE; + } + + auto* backendObject = GetBackendObject(state); + if (!backendObject) { MGLOG_E("activeBackendObject not initialized!"); return EGL_NO_SURFACE; } - activeBackendObject->SetWindowHandle({MG_Backend::WindowBackend::Android, reinterpret_cast(window)}); - activeBackendObject->InitWindowSurface(); - return (EGLSurface)1; + + const MG_Backend::WindowHandle windowHandle = { + .Backend = DetectWindowBackend(), + .Handle = ToVoidHandle(window), + }; + if (!backendObject->CreateEGLWindowSurface(windowHandle)) { + state->SetError(EGL_BAD_NATIVE_WINDOW); + return EGL_NO_SURFACE; + } + + return state->CreateWindowSurface(dpy, config, window, attrib_list); } EGLBoolean SwapBuffers(EGLDisplay dpy, EGLSurface draw) { - MGLOG_D("EGLImpl::SwapBuffers called with dpy=%p", dpy); - if (!MG_Backend::gBackendFunctionsTable.Present) { - MGLOG_E("MG_Backend::gBackendFunctionsTable.Present not initialized!"); + auto* state = GetState(); + if (!state) { + return EGL_FALSE; + } + if (!state->ValidateSurfaceOnDisplay(dpy, draw)) { + state->SetError(EGL_BAD_SURFACE); + return EGL_FALSE; + } + + auto* backendObject = GetBackendObject(state); + if (!backendObject) { + MGLOG_E("activeBackendObject not initialized!"); + return EGL_FALSE; + } + if (!backendObject->SwapEGLBuffers(dpy, draw)) { + state->SetError(EGL_BAD_SURFACE); return EGL_FALSE; } - MG_Backend::gBackendFunctionsTable.Present(); return EGL_TRUE; } EGLBoolean ChooseConfig(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs, EGLint config_size, EGLint* num_config) { - *num_config = 1; - return EGL_TRUE; + auto* state = GetState(); + if (!state) { + return EGL_FALSE; + } + return state->ChooseConfig(dpy, attrib_list, configs, config_size, num_config) ? EGL_TRUE : EGL_FALSE; } EGLContext CreateContext(EGLDisplay dpy, EGLConfig config, EGLContext shareCtx, const EGLint* attrib_list) { - return (EGLContext)1; + auto* state = GetState(); + if (!state) { + return EGL_NO_CONTEXT; + } + return state->CreateContext(dpy, config, shareCtx, attrib_list); } EGLBoolean Initialize(EGLDisplay dpy, EGLint* major, EGLint* minor) { - if (major) *major = 1; - if (minor) *minor = 5; + auto* state = GetState(); + if (!state) { + return EGL_FALSE; + } + if (!state->InitializeDisplay(dpy, major, minor)) { + return EGL_FALSE; + } + + auto* backendObject = GetBackendObject(state); + if (!backendObject) { + MGLOG_E("activeBackendObject not initialized!"); + return EGL_FALSE; + } + if (!backendObject->InitializeEGLDisplay(dpy, major, minor)) { + state->SetError(EGL_NOT_INITIALIZED); + return EGL_FALSE; + } return EGL_TRUE; } EGLDisplay GetDisplay(NativeDisplayType display) { - return (EGLDisplay)1; + auto* state = GetState(); + if (!state) { + return EGL_NO_DISPLAY; + } + return state->GetDisplay(display); } EGLint GetError() { - return EGL_SUCCESS; + auto* state = GetState(); + if (!state) { + return EGL_NOT_INITIALIZED; + } + return state->ConsumeError(); } EGLBoolean MakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) { - const auto& activeBackendObject = MG_Backend::pActiveBackendObject; - if (!activeBackendObject) { + auto* state = GetState(); + if (!state) { + return EGL_FALSE; + } + + const auto oldDisplay = state->GetCurrentDisplay(); + const auto oldDraw = state->GetCurrentSurface(EGL_DRAW); + const auto oldRead = state->GetCurrentSurface(EGL_READ); + const auto oldContext = state->GetCurrentContext(); + + if (!state->MakeCurrent(dpy, draw, read, ctx)) { + return EGL_FALSE; + } + + const Bool releaseCurrentRequest = + dpy == EGL_NO_DISPLAY && 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); + } + return EGL_TRUE; + } + + auto* backendObject = GetBackendObject(state); + if (!backendObject) { MGLOG_E("activeBackendObject not initialized!"); + state->MakeCurrent(oldDisplay, oldDraw, oldRead, oldContext); + return EGL_FALSE; + } + if (!backendObject->MakeEGLCurrent(dpy, draw, read, ctx)) { + state->SetError(EGL_BAD_ACCESS); + state->MakeCurrent(oldDisplay, oldDraw, oldRead, oldContext); return EGL_FALSE; } - activeBackendObject->InitCapabilities(); return EGL_TRUE; } EGLBoolean DestroyContext(EGLDisplay dpy, EGLContext ctx) { - return EGL_TRUE; + auto* state = GetState(); + if (!state) { + return EGL_FALSE; + } + return state->DestroyContext(dpy, ctx) ? EGL_TRUE : EGL_FALSE; } EGLBoolean DestroySurface(EGLDisplay dpy, EGLSurface surface) { - return EGL_TRUE; + auto* state = GetState(); + if (!state) { + return EGL_FALSE; + } + return state->DestroySurface(dpy, surface) ? EGL_TRUE : EGL_FALSE; } EGLBoolean Terminate(EGLDisplay dpy) { - return EGL_TRUE; + auto* state = GetState(); + if (!state) { + return EGL_FALSE; + } + return state->TerminateDisplay(dpy) ? EGL_TRUE : EGL_FALSE; } EGLBoolean ReleaseThread() { + auto* state = GetState(); + if (!state) { + return EGL_FALSE; + } + state->ReleaseThread(); return EGL_TRUE; } EGLContext GetCurrentContext() { - return (EGLContext)1; + auto* state = GetState(); + if (!state) { + return EGL_NO_CONTEXT; + } + return state->GetCurrentContext(); } EGLBoolean GetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint* value) { - if (attribute == EGL_NATIVE_VISUAL_ID) { -#if defined(ANDROID) - *value = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; + auto* state = GetState(); + if (!state) { + return EGL_FALSE; + } + return state->GetConfigAttrib(dpy, config, attribute, value) ? EGL_TRUE : EGL_FALSE; + } + + EGLBoolean BindAPI(EGLenum api) { + auto* state = GetState(); + if (!state) { + return EGL_FALSE; + } + switch (api) { + case EGL_OPENGL_API: + case EGL_OPENGL_ES_API: + case EGL_OPENVG_API: + state->SetBoundAPI(api); return EGL_TRUE; -#elif defined(__linux__) - *value = 0; - return EGL_TRUE; -#elif defined(_WIN32) - *value = 0; - return EGL_TRUE; -#else - *value = 0; + default: + state->SetError(EGL_BAD_PARAMETER); + return EGL_FALSE; + } + } + + EGLSurface GetCurrentSurface(EGLint readdraw) { + auto* state = GetState(); + if (!state) { + return EGL_NO_SURFACE; + } + return state->GetCurrentSurface(readdraw); + } + + EGLBoolean QuerySurface(EGLDisplay display, EGLSurface surface, EGLint attribute, EGLint* value) { + auto* state = GetState(); + if (!state) { + return EGL_FALSE; + } + return state->QuerySurface(display, surface, attribute, value) ? EGL_TRUE : EGL_FALSE; + } + + char const* QueryString(EGLDisplay display, EGLint name) { + auto* state = GetState(); + if (!state) { + return nullptr; + } + + if (display != EGL_NO_DISPLAY && !state->ValidateDisplay(display)) { + state->SetError(EGL_BAD_DISPLAY); + return nullptr; + } + + switch (name) { + case EGL_VENDOR: + return "MobileGL"; + case EGL_VERSION: + return "1.5 MobileGL"; + case EGL_CLIENT_APIS: + return "OpenGL OpenGL_ES"; + case EGL_EXTENSIONS: + return ""; + default: + state->SetError(EGL_BAD_PARAMETER); + return nullptr; + } + } + + EGLBoolean SwapInterval(EGLDisplay dpy, EGLint interval) { + auto* state = GetState(); + if (!state) { + return EGL_FALSE; + } + return state->SwapInterval(dpy, interval) ? EGL_TRUE : EGL_FALSE; + } + + EGLSurface CreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list) { + auto* state = GetState(); + if (!state) { + return EGL_NO_SURFACE; + } + return state->CreatePbufferSurface(dpy, config, attrib_list); + } + + EGLBoolean BindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) { + auto* state = GetState(); + if (!state) { + return EGL_FALSE; + } + if (!state->ValidateSurfaceOnDisplay(dpy, surface)) { + state->SetError(EGL_BAD_SURFACE); + return EGL_FALSE; + } + if (buffer != EGL_BACK_BUFFER) { + state->SetError(EGL_BAD_PARAMETER); return EGL_FALSE; -#endif } return EGL_TRUE; } - EGLBoolean BindAPI(EGLenum api) { - return EGL_TRUE; - } - - EGLSurface GetCurrentSurface(EGLint readdraw) { - return (EGLSurface)1; - } - - EGLBoolean QuerySurface(EGLDisplay display, EGLSurface surface, EGLint attribute, EGLint* value) { - return EGL_TRUE; - } - - char const* QueryString(EGLDisplay display, EGLint name) { - return ""; - } - - EGLBoolean SwapInterval(EGLDisplay dpy, EGLint interval) { - return EGL_TRUE; - } - - EGLSurface CreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list) { - return (EGLSurface)1; - } - - EGLBoolean BindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) { - return EGL_TRUE; - } - EGLBoolean ReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) { + auto* state = GetState(); + if (!state) { + return EGL_FALSE; + } + if (!state->ValidateSurfaceOnDisplay(dpy, surface)) { + state->SetError(EGL_BAD_SURFACE); + return EGL_FALSE; + } + if (buffer != EGL_BACK_BUFFER) { + state->SetError(EGL_BAD_PARAMETER); + return EGL_FALSE; + } return EGL_TRUE; } EGLBoolean CopyBuffers(EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target) { + auto* state = GetState(); + if (!state) { + return EGL_FALSE; + } + if (!state->ValidateSurfaceOnDisplay(dpy, surface)) { + state->SetError(EGL_BAD_SURFACE); + return EGL_FALSE; + } + if (IsNullNativeHandle(target)) { + state->SetError(EGL_BAD_NATIVE_PIXMAP); + return EGL_FALSE; + } return EGL_TRUE; } EGLSurface CreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint* attrib_list) { - return (EGLSurface)1; + auto* state = GetState(); + if (!state) { + return EGL_NO_SURFACE; + } + return state->CreatePbufferFromClientBuffer(dpy, buftype, buffer, config, attrib_list); } EGLSurface CreatePixmapSurface(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint* attrib_list) { - return (EGLSurface)1; + auto* state = GetState(); + if (!state) { + return EGL_NO_SURFACE; + } + return state->CreatePixmapSurface(dpy, config, pixmap, attrib_list); } EGLBoolean GetConfigs(EGLDisplay dpy, EGLConfig* configs, EGLint config_size, EGLint* num_config) { - if (num_config) { - *num_config = 1; + auto* state = GetState(); + if (!state) { + return EGL_FALSE; } - if (configs && config_size > 0) { - configs[0] = (EGLConfig)1; - } - return EGL_TRUE; + return state->GetConfigs(dpy, configs, config_size, num_config) ? EGL_TRUE : EGL_FALSE; } EGLDisplay GetCurrentDisplay() { - return (EGLDisplay)1; + auto* state = GetState(); + if (!state) { + return EGL_NO_DISPLAY; + } + return state->GetCurrentDisplay(); } EGLenum QueryAPI() { - return EGL_OPENGL_API; + auto* state = GetState(); + if (!state) { + return EGL_OPENGL_API; + } + return state->GetBoundAPI(); } EGLBoolean QueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint* value) { - if (value) { - *value = 1; + auto* state = GetState(); + if (!state) { + return EGL_FALSE; } - return EGL_TRUE; + return state->QueryContext(dpy, ctx, attribute, value) ? EGL_TRUE : EGL_FALSE; } EGLBoolean SurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) { - return EGL_TRUE; + (void)value; + + auto* state = GetState(); + if (!state) { + return EGL_FALSE; + } + if (!state->ValidateSurfaceOnDisplay(dpy, surface)) { + state->SetError(EGL_BAD_SURFACE); + return EGL_FALSE; + } + + switch (attribute) { + case EGL_MIPMAP_LEVEL: + case EGL_SWAP_BEHAVIOR: + case EGL_TEXTURE_FORMAT: + case EGL_TEXTURE_TARGET: + case EGL_MIPMAP_TEXTURE: + return EGL_TRUE; + default: + state->SetError(EGL_BAD_ATTRIBUTE); + return EGL_FALSE; + } } EGLBoolean WaitClient() { @@ -191,60 +465,132 @@ namespace MobileGL::MG_Impl::EGLImpl { } EGLBoolean WaitNative(EGLint engine) { + (void)engine; return EGL_TRUE; } EGLSync CreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib* attrib_list) { - return reinterpret_cast(0x1); + auto* state = GetState(); + if (!state) { + return EGL_NO_SYNC; + } + return state->CreateSync(dpy, type, attrib_list); } - EGLBoolean DestroySync(void* dpy, void* sync) { - return EGL_TRUE; + EGLBoolean DestroySync(EGLDisplay dpy, EGLSync sync) { + auto* state = GetState(); + if (!state) { + return EGL_FALSE; + } + return state->DestroySync(dpy, sync) ? EGL_TRUE : EGL_FALSE; } EGLint ClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout) { - return EGL_CONDITION_SATISFIED; + auto* state = GetState(); + if (!state) { + return EGL_FALSE; + } + return state->ClientWaitSync(dpy, sync, flags, timeout); } EGLBoolean GetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib* value) { - if (value) { - *value = 1; + auto* state = GetState(); + if (!state) { + return EGL_FALSE; } - return EGL_TRUE; + return state->GetSyncAttrib(dpy, sync, attribute, value) ? EGL_TRUE : EGL_FALSE; } EGLImage CreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib* attrib_list) { - return reinterpret_cast(0x1); + auto* state = GetState(); + if (!state) { + return EGL_NO_IMAGE; + } + return state->CreateImage(dpy, ctx, target, buffer, attrib_list); } EGLBoolean DestroyImage(EGLDisplay dpy, EGLImage image) { - return EGL_TRUE; + auto* state = GetState(); + if (!state) { + return EGL_FALSE; + } + return state->DestroyImage(dpy, image) ? EGL_TRUE : EGL_FALSE; } EGLDisplay GetPlatformDisplay(EGLenum platform, void* native_display, const EGLAttrib* attrib_list) { - return reinterpret_cast(0x1); + (void)attrib_list; + + auto* state = GetState(); + if (!state) { + return EGL_NO_DISPLAY; + } + return state->GetPlatformDisplay(platform, native_display); } EGLSurface CreatePlatformWindowSurface(EGLDisplay dpy, EGLConfig config, void* native_window, const EGLAttrib* attrib_list) { - return reinterpret_cast(0x1); + auto* state = GetState(); + if (!state) { + return EGL_NO_SURFACE; + } + if (native_window == nullptr) { + state->SetError(EGL_BAD_NATIVE_WINDOW); + return EGL_NO_SURFACE; + } + if (!state->IsDisplayInitialized(dpy)) { + state->SetError(EGL_NOT_INITIALIZED); + return EGL_NO_SURFACE; + } + if (!state->ValidateConfigOnDisplay(dpy, config)) { + state->SetError(EGL_BAD_CONFIG); + return EGL_NO_SURFACE; + } + + auto* backendObject = GetBackendObject(state); + if (!backendObject) { + MGLOG_E("activeBackendObject not initialized!"); + return EGL_NO_SURFACE; + } + + const MG_Backend::WindowHandle windowHandle = { + .Backend = DetectWindowBackend(), + .Handle = native_window, + }; + if (!backendObject->CreateEGLWindowSurface(windowHandle)) { + state->SetError(EGL_BAD_NATIVE_WINDOW); + return EGL_NO_SURFACE; + } + + return state->CreatePlatformWindowSurface(dpy, config, native_window, attrib_list); } EGLSurface CreatePlatformPixmapSurface(EGLDisplay dpy, EGLConfig config, void* native_pixmap, const EGLAttrib* attrib_list) { - return reinterpret_cast(0x1); + auto* state = GetState(); + if (!state) { + return EGL_NO_SURFACE; + } + return state->CreatePlatformPixmapSurface(dpy, config, native_pixmap, attrib_list); } - EGLBoolean WaitSync(void* dpy, void* sync, int flags) { - return EGL_TRUE; + EGLBoolean WaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags) { + auto* state = GetState(); + if (!state) { + return EGL_FALSE; + } + return state->WaitSync(dpy, sync, flags) ? EGL_TRUE : EGL_FALSE; } __eglMustCastToProperFunctionPointerType GetProcAddress(const char* name) { + if (!name) { + return nullptr; + } + MGLOG_D("eglGetProcAddress(%s)", name); void* proc = MG_Impl::GetProcAddress(name); if (!proc) { - MGLOG_W("Failed to get function: %s", (const char*)name); + MGLOG_W("Failed to get function: %s", name); return nullptr; } return (__eglMustCastToProperFunctionPointerType)proc; diff --git a/MobileGL/MG_Impl/EGLImpl/EGLImpl.h b/MobileGL/MG_Impl/EGLImpl/EGLImpl.h index f4787415..02d72f74 100644 --- a/MobileGL/MG_Impl/EGLImpl/EGLImpl.h +++ b/MobileGL/MG_Impl/EGLImpl/EGLImpl.h @@ -48,7 +48,7 @@ namespace MobileGL::MG_Impl::EGLImpl { EGLBoolean WaitGL(); EGLBoolean WaitNative(EGLint engine); EGLSync CreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib* attrib_list); - EGLBoolean DestroySync(void* dpy, void* sync); + EGLBoolean DestroySync(EGLDisplay dpy, EGLSync sync); EGLint ClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout); EGLBoolean GetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib* value); EGLImage CreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, @@ -59,6 +59,6 @@ namespace MobileGL::MG_Impl::EGLImpl { const EGLAttrib* attrib_list); EGLSurface CreatePlatformPixmapSurface(EGLDisplay dpy, EGLConfig config, void* native_pixmap, const EGLAttrib* attrib_list); - EGLBoolean WaitSync(void* dpy, void* sync, int flags); + EGLBoolean WaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags); __eglMustCastToProperFunctionPointerType GetProcAddress(const char* name); } // namespace MobileGL::MG_Impl::EGLImpl diff --git a/MobileGL/MG_State/EGLState/Core.cpp b/MobileGL/MG_State/EGLState/Core.cpp new file mode 100644 index 00000000..6088ac5b --- /dev/null +++ b/MobileGL/MG_State/EGLState/Core.cpp @@ -0,0 +1,1135 @@ +// MobileGL - MobileGL/MG_State/EGLState/Core.cpp +// Copyright (c) 2025-2026 MobileGL-Dev +// Licensed under the GNU Lesser General Public License v3.0: +// https://www.gnu.org/licenses/gpl-3.0.txt +// https://www.gnu.org/licenses/lgpl-3.0.txt +// SPDX-License-Identifier: LGPL-3.0-only +// End of Source File Header + +#include "Core.h" + +namespace MobileGL { + namespace MG_State { + namespace EGLState { + namespace { + constexpr EGLint EGL_DISPLAY_MAJOR_VERSION = 1; + constexpr EGLint EGL_DISPLAY_MINOR_VERSION = 5; + } // namespace + + SizeT EGLContext::DisplayLookupHasher::operator()(const DisplayLookupKey& key) const { + const auto nativeHash = std::hash{}(key.NativeDisplayKey); + const auto platformHash = std::hash{}(static_cast(key.Platform)); + return nativeHash ^ (platformHash + 0x9e3779b97f4a7c15ull + (nativeHash << 6) + (nativeHash >> 2)); + } + + std::thread::id EGLContext::CurrentThreadKey() { + return std::this_thread::get_id(); + } + + void EGLContext::SetError(EGLint errorCode) { + if (errorCode == EGL_SUCCESS) { + return; + } + const std::lock_guard lock(m_mutex); + m_threadErrors[CurrentThreadKey()] = errorCode; + } + + EGLint EGLContext::ConsumeError() { + const std::lock_guard lock(m_mutex); + const auto threadKey = CurrentThreadKey(); + auto errorIt = m_threadErrors.find(threadKey); + if (errorIt == m_threadErrors.end()) { + return EGL_SUCCESS; + } + + const EGLint error = errorIt->second; + m_threadErrors.erase(errorIt); + return error; + } + + void EGLContext::SetBoundAPI(EGLenum api) { + const std::lock_guard lock(m_mutex); + m_threadBoundAPI[CurrentThreadKey()] = api; + } + + EGLenum EGLContext::GetBoundAPI() const { + const std::lock_guard lock(m_mutex); + auto apiIt = m_threadBoundAPI.find(CurrentThreadKey()); + if (apiIt == m_threadBoundAPI.end()) { + return EGL_OPENGL_API; + } + return apiIt->second; + } + + Optional EGLContext::ParseAttribValue(const EGLint* attribList, EGLint attrib) { + if (!attribList) { + return Nullopt; + } + for (SizeT i = 0; attribList[i] != EGL_NONE; i += 2) { + if (attribList[i] == attrib) { + return attribList[i + 1]; + } + } + return Nullopt; + } + + Optional EGLContext::ParseAttribValue(const EGLAttrib* attribList, EGLint attrib) { + if (!attribList) { + return Nullopt; + } + for (SizeT i = 0; attribList[i] != EGL_NONE; i += 2) { + if (attribList[i] == static_cast(attrib)) { + return attribList[i + 1]; + } + } + return Nullopt; + } + + EGLContext::EGLDisplayHandle EGLContext::GetOrCreateDisplay(Uint64 nativeDisplayKey, EGLenum platform) { + const DisplayLookupKey key = { + .NativeDisplayKey = nativeDisplayKey, + .Platform = platform, + }; + + auto lookupIt = m_displayLookup.find(key); + if (lookupIt != m_displayLookup.end()) { + return lookupIt->second; + } + + const auto display = EncodeHandle(m_nextDisplayHandle++); + DisplayObject displayObject = { + .NativeDisplayKey = nativeDisplayKey, + .Platform = platform, + .Initialized = false, + .MajorVersion = EGL_DISPLAY_MAJOR_VERSION, + .MinorVersion = EGL_DISPLAY_MINOR_VERSION, + .SwapInterval = 1, + }; + + const auto config = CreateDefaultConfig(display); + displayObject.Configs.push_back(config); + + m_displays[display] = displayObject; + m_displayLookup[key] = display; + return display; + } + + EGLContext::EGLConfigHandle EGLContext::CreateDefaultConfig(EGLDisplayHandle display) { + const auto config = EncodeHandle(m_nextConfigHandle++); + ConfigObject cfg = { + .Display = display, + .ConfigId = 1, + .RedSize = 8, + .GreenSize = 8, + .BlueSize = 8, + .AlphaSize = 8, + .DepthSize = 24, + .StencilSize = 8, + .SurfaceType = EGL_WINDOW_BIT | EGL_PBUFFER_BIT | EGL_PIXMAP_BIT, + .RenderableType = EGL_OPENGL_BIT | EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT, + .MinSwapInterval = 0, + .MaxSwapInterval = 4, + .NativeVisualId = 0, + }; +#if defined(ANDROID) || defined(__ANDROID__) + cfg.NativeVisualId = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; +#endif + m_configs[config] = cfg; + return config; + } + + EGLContext::DisplayObject* EGLContext::TryGetDisplay(EGLDisplayHandle display) { + auto it = m_displays.find(display); + return it == m_displays.end() ? nullptr : &it->second; + } + + const EGLContext::DisplayObject* EGLContext::TryGetDisplay(EGLDisplayHandle display) const { + auto it = m_displays.find(display); + return it == m_displays.end() ? nullptr : &it->second; + } + + const EGLContext::ConfigObject* EGLContext::TryGetConfig(EGLConfigHandle config) const { + auto it = m_configs.find(config); + return it == m_configs.end() ? nullptr : &it->second; + } + + const EGLContext::ContextObject* EGLContext::TryGetContext(EGLContextHandle context) const { + auto it = m_contexts.find(context); + return it == m_contexts.end() ? nullptr : &it->second; + } + + const EGLContext::SurfaceObject* EGLContext::TryGetSurface(EGLSurfaceHandle surface) const { + auto it = m_surfaces.find(surface); + return it == m_surfaces.end() ? nullptr : &it->second; + } + + const EGLContext::SyncObject* EGLContext::TryGetSync(EGLSyncHandle sync) const { + auto it = m_syncs.find(sync); + return it == m_syncs.end() ? nullptr : &it->second; + } + + const EGLContext::ImageObject* EGLContext::TryGetImage(EGLImageHandle image) const { + auto it = m_images.find(image); + return it == m_images.end() ? nullptr : &it->second; + } + + void EGLContext::ReleaseThreadUnlocked(const std::thread::id& threadKey) { + auto currentIt = m_threadCurrents.find(threadKey); + if (currentIt == m_threadCurrents.end()) { + return; + } + + const EGLContextHandle context = currentIt->second.Context; + if (context != nullptr) { + auto ownerIt = m_contextOwners.find(context); + if (ownerIt != m_contextOwners.end() && ownerIt->second == threadKey) { + m_contextOwners.erase(ownerIt); + } + } + m_threadCurrents.erase(currentIt); + } + + void EGLContext::ReleaseDisplayObjects(EGLDisplayHandle display) { + Vector removedContexts; + for (auto contextIt = m_contexts.begin(); contextIt != m_contexts.end();) { + if (contextIt->second.Display == display) { + removedContexts.push_back(contextIt->first); + contextIt = m_contexts.erase(contextIt); + } else { + ++contextIt; + } + } + + for (const auto context : removedContexts) { + m_contextOwners.erase(context); + } + + for (auto surfaceIt = m_surfaces.begin(); surfaceIt != m_surfaces.end();) { + if (surfaceIt->second.Display == display) { + surfaceIt = m_surfaces.erase(surfaceIt); + } else { + ++surfaceIt; + } + } + + for (auto syncIt = m_syncs.begin(); syncIt != m_syncs.end();) { + if (syncIt->second.Display == display) { + syncIt = m_syncs.erase(syncIt); + } else { + ++syncIt; + } + } + + for (auto imageIt = m_images.begin(); imageIt != m_images.end();) { + if (imageIt->second.Display == display) { + imageIt = m_images.erase(imageIt); + } else { + ++imageIt; + } + } + + for (auto threadIt = m_threadCurrents.begin(); threadIt != m_threadCurrents.end();) { + if (threadIt->second.Display == display) { + if (threadIt->second.Context != nullptr) { + auto ownerIt = m_contextOwners.find(threadIt->second.Context); + if (ownerIt != m_contextOwners.end() && ownerIt->second == threadIt->first) { + m_contextOwners.erase(ownerIt); + } + } + threadIt = m_threadCurrents.erase(threadIt); + } else { + ++threadIt; + } + } + } + + EGLContext::EGLDisplayHandle EGLContext::GetDisplay(NativeDisplayType nativeDisplay) { + const std::lock_guard lock(m_mutex); + return GetOrCreateDisplay(ToNativeKey(nativeDisplay), EGL_NONE); + } + + EGLContext::EGLDisplayHandle EGLContext::GetPlatformDisplay(EGLenum platform, void* nativeDisplay) { + const std::lock_guard lock(m_mutex); + return GetOrCreateDisplay(ToNativeKey(nativeDisplay), platform); + } + + Bool EGLContext::ValidateDisplay(EGLDisplayHandle display) const { + const std::lock_guard lock(m_mutex); + return m_displays.find(display) != m_displays.end(); + } + + Bool EGLContext::IsDisplayInitialized(EGLDisplayHandle display) const { + const std::lock_guard lock(m_mutex); + const auto* displayObject = TryGetDisplay(display); + return displayObject && displayObject->Initialized; + } + + Bool EGLContext::InitializeDisplay(EGLDisplayHandle display, EGLint* major, EGLint* minor) { + const std::lock_guard lock(m_mutex); + auto* displayObject = TryGetDisplay(display); + if (!displayObject) { + SetError(EGL_BAD_DISPLAY); + return false; + } + + displayObject->Initialized = true; + displayObject->MajorVersion = EGL_DISPLAY_MAJOR_VERSION; + displayObject->MinorVersion = EGL_DISPLAY_MINOR_VERSION; + + if (major) { + *major = displayObject->MajorVersion; + } + if (minor) { + *minor = displayObject->MinorVersion; + } + return true; + } + + Bool EGLContext::TerminateDisplay(EGLDisplayHandle display) { + const std::lock_guard lock(m_mutex); + auto* displayObject = TryGetDisplay(display); + if (!displayObject) { + SetError(EGL_BAD_DISPLAY); + return false; + } + + ReleaseDisplayObjects(display); + displayObject->Initialized = false; + return true; + } + + Bool EGLContext::ChooseConfig(EGLDisplayHandle display, const EGLint* attribList, EGLConfigHandle* configs, + EGLint configSize, EGLint* numConfig) { + const std::lock_guard lock(m_mutex); + (void)attribList; + auto* displayObject = TryGetDisplay(display); + if (!displayObject) { + SetError(EGL_BAD_DISPLAY); + return false; + } + if (!displayObject->Initialized) { + SetError(EGL_NOT_INITIALIZED); + return false; + } + if (!numConfig) { + SetError(EGL_BAD_PARAMETER); + return false; + } + + *numConfig = static_cast(displayObject->Configs.size()); + if (!configs || configSize <= 0) { + return true; + } + + const SizeT copyCount = std::min(static_cast(configSize), displayObject->Configs.size()); + for (SizeT i = 0; i < copyCount; ++i) { + configs[i] = displayObject->Configs[i]; + } + return true; + } + + Bool EGLContext::GetConfigs(EGLDisplayHandle display, EGLConfigHandle* configs, EGLint configSize, + EGLint* numConfig) { + const std::lock_guard lock(m_mutex); + return ChooseConfig(display, nullptr, configs, configSize, numConfig); + } + + Bool EGLContext::ValidateConfig(EGLConfigHandle config) const { + const std::lock_guard lock(m_mutex); + return m_configs.find(config) != m_configs.end(); + } + + Bool EGLContext::ValidateConfigOnDisplay(EGLDisplayHandle display, EGLConfigHandle config) const { + const std::lock_guard lock(m_mutex); + const auto* cfg = TryGetConfig(config); + return cfg && cfg->Display == display; + } + + Bool EGLContext::GetConfigAttrib(EGLDisplayHandle display, EGLConfigHandle config, EGLint attribute, + EGLint* value) const { + const std::lock_guard lock(m_mutex); + if (!value) { + const_cast(this)->SetError(EGL_BAD_PARAMETER); + return false; + } + const auto* cfg = TryGetConfig(config); + if (!cfg) { + const_cast(this)->SetError(EGL_BAD_CONFIG); + return false; + } + if (cfg->Display != display) { + const_cast(this)->SetError(EGL_BAD_MATCH); + return false; + } + + switch (attribute) { + case EGL_CONFIG_ID: + *value = cfg->ConfigId; + return true; + case EGL_RED_SIZE: + *value = cfg->RedSize; + return true; + case EGL_GREEN_SIZE: + *value = cfg->GreenSize; + return true; + case EGL_BLUE_SIZE: + *value = cfg->BlueSize; + return true; + case EGL_ALPHA_SIZE: + *value = cfg->AlphaSize; + return true; + case EGL_DEPTH_SIZE: + *value = cfg->DepthSize; + return true; + case EGL_STENCIL_SIZE: + *value = cfg->StencilSize; + return true; + case EGL_SURFACE_TYPE: + *value = cfg->SurfaceType; + return true; + case EGL_RENDERABLE_TYPE: + *value = cfg->RenderableType; + return true; + case EGL_CONFORMANT: + *value = cfg->RenderableType; + return true; + case EGL_MIN_SWAP_INTERVAL: + *value = cfg->MinSwapInterval; + return true; + case EGL_MAX_SWAP_INTERVAL: + *value = cfg->MaxSwapInterval; + return true; + case EGL_NATIVE_VISUAL_ID: + *value = cfg->NativeVisualId; + return true; + default: + const_cast(this)->SetError(EGL_BAD_ATTRIBUTE); + return false; + } + } + + EGLContext::EGLContextHandle EGLContext::CreateContext(EGLDisplayHandle display, EGLConfigHandle config, + EGLContextHandle shareCtx, + const EGLint* attribList) { + const std::lock_guard lock(m_mutex); + const auto* displayObject = TryGetDisplay(display); + if (!displayObject) { + SetError(EGL_BAD_DISPLAY); + return nullptr; + } + if (!displayObject->Initialized) { + SetError(EGL_NOT_INITIALIZED); + return nullptr; + } + if (!ValidateConfigOnDisplay(display, config)) { + SetError(EGL_BAD_CONFIG); + return nullptr; + } + if (shareCtx != nullptr && !ValidateContextOnDisplay(display, shareCtx)) { + SetError(EGL_BAD_CONTEXT); + return nullptr; + } + + ContextObject contextObject = { + .Display = display, + .Config = config, + .SharedContext = shareCtx, + .ClientAPI = GetBoundAPI(), + .ClientVersion = 1, + .MajorVersion = 1, + .MinorVersion = 0, + }; + + if (auto value = ParseAttribValue(attribList, EGL_CONTEXT_CLIENT_VERSION); value) { + contextObject.ClientVersion = *value; + contextObject.MajorVersion = *value; + } + if (auto value = ParseAttribValue(attribList, EGL_CONTEXT_MAJOR_VERSION); value) { + contextObject.MajorVersion = *value; + } + if (auto value = ParseAttribValue(attribList, EGL_CONTEXT_MINOR_VERSION); value) { + contextObject.MinorVersion = *value; + } + + const auto context = EncodeHandle(m_nextContextHandle++); + m_contexts[context] = contextObject; + return context; + } + + Bool EGLContext::DestroyContext(EGLDisplayHandle display, EGLContextHandle context) { + const std::lock_guard lock(m_mutex); + auto contextIt = m_contexts.find(context); + if (contextIt == m_contexts.end()) { + SetError(EGL_BAD_CONTEXT); + return false; + } + if (contextIt->second.Display != display) { + SetError(EGL_BAD_MATCH); + return false; + } + + auto ownerIt = m_contextOwners.find(context); + if (ownerIt != m_contextOwners.end()) { + SetError(EGL_BAD_ACCESS); + return false; + } + m_contexts.erase(contextIt); + return true; + } + + Bool EGLContext::QueryContext(EGLDisplayHandle display, EGLContextHandle context, EGLint attribute, + EGLint* value) const { + const std::lock_guard lock(m_mutex); + if (!value) { + const_cast(this)->SetError(EGL_BAD_PARAMETER); + return false; + } + const auto* contextObject = TryGetContext(context); + if (!contextObject) { + const_cast(this)->SetError(EGL_BAD_CONTEXT); + return false; + } + if (contextObject->Display != display) { + const_cast(this)->SetError(EGL_BAD_MATCH); + return false; + } + + switch (attribute) { + case EGL_CONTEXT_CLIENT_TYPE: + *value = static_cast(contextObject->ClientAPI); + return true; + case EGL_CONTEXT_CLIENT_VERSION: +#if EGL_CONTEXT_MAJOR_VERSION != EGL_CONTEXT_CLIENT_VERSION + case EGL_CONTEXT_MAJOR_VERSION: +#endif + *value = contextObject->MajorVersion; + return true; + case EGL_CONTEXT_MINOR_VERSION: + *value = contextObject->MinorVersion; + return true; + case EGL_CONFIG_ID: { + const auto* cfg = TryGetConfig(contextObject->Config); + if (!cfg) { + const_cast(this)->SetError(EGL_BAD_CONFIG); + return false; + } + *value = cfg->ConfigId; + return true; + } + default: + const_cast(this)->SetError(EGL_BAD_ATTRIBUTE); + return false; + } + } + + Bool EGLContext::ValidateContext(EGLContextHandle context) const { + const std::lock_guard lock(m_mutex); + return m_contexts.find(context) != m_contexts.end(); + } + + Bool EGLContext::ValidateContextOnDisplay(EGLDisplayHandle display, EGLContextHandle context) const { + const std::lock_guard lock(m_mutex); + const auto* ctx = TryGetContext(context); + return ctx && ctx->Display == display; + } + + EGLContext::EGLSurfaceHandle EGLContext::CreateWindowSurface(EGLDisplayHandle display, + EGLConfigHandle config, + NativeWindowType window, + const EGLint* attribList) { + const std::lock_guard lock(m_mutex); + (void)attribList; + if (!IsDisplayInitialized(display)) { + SetError(EGL_NOT_INITIALIZED); + return EGL_NO_SURFACE; + } + if (!ValidateConfigOnDisplay(display, config)) { + SetError(EGL_BAD_CONFIG); + return EGL_NO_SURFACE; + } + if (ToNativeKey(window) == 0) { + SetError(EGL_BAD_NATIVE_WINDOW); + return EGL_NO_SURFACE; + } + + const auto surface = EncodeHandle(m_nextSurfaceHandle++); + m_surfaces[surface] = SurfaceObject{ + .Display = display, + .Config = config, + .Type = SurfaceType::Window, + .NativeHandleKey = ToNativeKey(window), + .ClientBuffer = nullptr, + .BufferType = EGL_NONE, + }; + return surface; + } + + EGLContext::EGLSurfaceHandle EGLContext::CreatePbufferSurface(EGLDisplayHandle display, + EGLConfigHandle config, + const EGLint* attribList) { + const std::lock_guard lock(m_mutex); + if (!IsDisplayInitialized(display)) { + SetError(EGL_NOT_INITIALIZED); + return EGL_NO_SURFACE; + } + if (!ValidateConfigOnDisplay(display, config)) { + SetError(EGL_BAD_CONFIG); + return EGL_NO_SURFACE; + } + + EGLint width = 1; + EGLint height = 1; + if (auto value = ParseAttribValue(attribList, EGL_WIDTH); value) { + width = *value; + } + if (auto value = ParseAttribValue(attribList, EGL_HEIGHT); value) { + height = *value; + } + if (width < 0 || height < 0) { + SetError(EGL_BAD_ATTRIBUTE); + return EGL_NO_SURFACE; + } + + const auto surface = EncodeHandle(m_nextSurfaceHandle++); + m_surfaces[surface] = SurfaceObject{ + .Display = display, + .Config = config, + .Type = SurfaceType::Pbuffer, + .NativeHandleKey = 0, + .ClientBuffer = nullptr, + .BufferType = EGL_NONE, + .Width = width, + .Height = height, + .TextureFormat = ParseAttribValue(attribList, EGL_TEXTURE_FORMAT).value_or(EGL_NO_TEXTURE), + .TextureTarget = ParseAttribValue(attribList, EGL_TEXTURE_TARGET).value_or(EGL_NO_TEXTURE), + .MipmapLevel = ParseAttribValue(attribList, EGL_MIPMAP_LEVEL).value_or(0), + .MipmapTexture = ParseAttribValue(attribList, EGL_MIPMAP_TEXTURE).value_or(EGL_FALSE), + }; + return surface; + } + + EGLContext::EGLSurfaceHandle EGLContext::CreatePixmapSurface(EGLDisplayHandle display, + EGLConfigHandle config, + EGLNativePixmapType pixmap, + const EGLint* attribList) { + const std::lock_guard lock(m_mutex); + (void)attribList; + if (!IsDisplayInitialized(display)) { + SetError(EGL_NOT_INITIALIZED); + return EGL_NO_SURFACE; + } + if (!ValidateConfigOnDisplay(display, config)) { + SetError(EGL_BAD_CONFIG); + return EGL_NO_SURFACE; + } + if (ToNativeKey(pixmap) == 0) { + SetError(EGL_BAD_NATIVE_PIXMAP); + return EGL_NO_SURFACE; + } + + const auto surface = EncodeHandle(m_nextSurfaceHandle++); + m_surfaces[surface] = SurfaceObject{ + .Display = display, + .Config = config, + .Type = SurfaceType::Pixmap, + .NativeHandleKey = ToNativeKey(pixmap), + .ClientBuffer = nullptr, + .BufferType = EGL_NONE, + }; + return surface; + } + + EGLContext::EGLSurfaceHandle EGLContext::CreatePbufferFromClientBuffer(EGLDisplayHandle display, + EGLenum bufferType, + EGLClientBuffer buffer, + EGLConfigHandle config, + const EGLint* attribList) { + const std::lock_guard lock(m_mutex); + if (!IsDisplayInitialized(display)) { + SetError(EGL_NOT_INITIALIZED); + return EGL_NO_SURFACE; + } + if (!ValidateConfigOnDisplay(display, config)) { + SetError(EGL_BAD_CONFIG); + return EGL_NO_SURFACE; + } + if (!buffer) { + SetError(EGL_BAD_PARAMETER); + return EGL_NO_SURFACE; + } + + EGLint width = 1; + EGLint height = 1; + if (auto value = ParseAttribValue(attribList, EGL_WIDTH); value) { + width = *value; + } + if (auto value = ParseAttribValue(attribList, EGL_HEIGHT); value) { + height = *value; + } + + const auto surface = EncodeHandle(m_nextSurfaceHandle++); + m_surfaces[surface] = SurfaceObject{ + .Display = display, + .Config = config, + .Type = SurfaceType::PbufferFromClientBuffer, + .NativeHandleKey = 0, + .ClientBuffer = buffer, + .BufferType = bufferType, + .Width = width, + .Height = height, + }; + return surface; + } + + EGLContext::EGLSurfaceHandle EGLContext::CreatePlatformWindowSurface(EGLDisplayHandle display, + EGLConfigHandle config, + void* nativeWindow, + const EGLAttrib* attribList) { + const std::lock_guard lock(m_mutex); + if (!IsDisplayInitialized(display)) { + SetError(EGL_NOT_INITIALIZED); + return EGL_NO_SURFACE; + } + if (!ValidateConfigOnDisplay(display, config)) { + SetError(EGL_BAD_CONFIG); + return EGL_NO_SURFACE; + } + if (ToNativeKey(nativeWindow) == 0) { + SetError(EGL_BAD_NATIVE_WINDOW); + return EGL_NO_SURFACE; + } + + const auto surface = EncodeHandle(m_nextSurfaceHandle++); + m_surfaces[surface] = SurfaceObject{ + .Display = display, + .Config = config, + .Type = SurfaceType::PlatformWindow, + .NativeHandleKey = ToNativeKey(nativeWindow), + .ClientBuffer = nullptr, + .BufferType = EGL_NONE, + .Width = static_cast(ParseAttribValue(attribList, EGL_WIDTH).value_or(0)), + .Height = static_cast(ParseAttribValue(attribList, EGL_HEIGHT).value_or(0)), + }; + return surface; + } + + EGLContext::EGLSurfaceHandle EGLContext::CreatePlatformPixmapSurface(EGLDisplayHandle display, + EGLConfigHandle config, + void* nativePixmap, + const EGLAttrib* attribList) { + const std::lock_guard lock(m_mutex); + (void)attribList; + if (!IsDisplayInitialized(display)) { + SetError(EGL_NOT_INITIALIZED); + return EGL_NO_SURFACE; + } + if (!ValidateConfigOnDisplay(display, config)) { + SetError(EGL_BAD_CONFIG); + return EGL_NO_SURFACE; + } + if (ToNativeKey(nativePixmap) == 0) { + SetError(EGL_BAD_NATIVE_PIXMAP); + return EGL_NO_SURFACE; + } + + const auto surface = EncodeHandle(m_nextSurfaceHandle++); + m_surfaces[surface] = SurfaceObject{ + .Display = display, + .Config = config, + .Type = SurfaceType::PlatformPixmap, + .NativeHandleKey = ToNativeKey(nativePixmap), + .ClientBuffer = nullptr, + .BufferType = EGL_NONE, + }; + return surface; + } + + Bool EGLContext::DestroySurface(EGLDisplayHandle display, EGLSurfaceHandle surface) { + const std::lock_guard lock(m_mutex); + auto surfaceIt = m_surfaces.find(surface); + if (surfaceIt == m_surfaces.end()) { + SetError(EGL_BAD_SURFACE); + return false; + } + if (surfaceIt->second.Display != display) { + SetError(EGL_BAD_MATCH); + 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; + } + m_surfaces.erase(surfaceIt); + return true; + } + + Bool EGLContext::QuerySurface(EGLDisplayHandle display, EGLSurfaceHandle surface, EGLint attribute, + EGLint* value) const { + const std::lock_guard lock(m_mutex); + if (!value) { + const_cast(this)->SetError(EGL_BAD_PARAMETER); + return false; + } + const auto* surfaceObject = TryGetSurface(surface); + if (!surfaceObject) { + const_cast(this)->SetError(EGL_BAD_SURFACE); + return false; + } + if (surfaceObject->Display != display) { + const_cast(this)->SetError(EGL_BAD_MATCH); + return false; + } + + switch (attribute) { + case EGL_WIDTH: + *value = surfaceObject->Width; + return true; + case EGL_HEIGHT: + *value = surfaceObject->Height; + return true; + case EGL_TEXTURE_FORMAT: + *value = surfaceObject->TextureFormat; + return true; + case EGL_TEXTURE_TARGET: + *value = surfaceObject->TextureTarget; + return true; + case EGL_MIPMAP_LEVEL: + *value = surfaceObject->MipmapLevel; + return true; + case EGL_MIPMAP_TEXTURE: + *value = surfaceObject->MipmapTexture; + return true; + case EGL_RENDER_BUFFER: + *value = surfaceObject->RenderBuffer; + return true; + case EGL_SWAP_BEHAVIOR: + *value = surfaceObject->SwapBehavior; + return true; + case EGL_CONFIG_ID: { + const auto* cfg = TryGetConfig(surfaceObject->Config); + if (!cfg) { + const_cast(this)->SetError(EGL_BAD_CONFIG); + return false; + } + *value = cfg->ConfigId; + return true; + } + default: + const_cast(this)->SetError(EGL_BAD_ATTRIBUTE); + return false; + } + } + + Bool EGLContext::ValidateSurface(EGLSurfaceHandle surface) const { + const std::lock_guard lock(m_mutex); + return m_surfaces.find(surface) != m_surfaces.end(); + } + + Bool EGLContext::ValidateSurfaceOnDisplay(EGLDisplayHandle display, EGLSurfaceHandle surface) const { + const std::lock_guard lock(m_mutex); + const auto* surf = TryGetSurface(surface); + return surf && surf->Display == display; + } + + Bool EGLContext::SwapInterval(EGLDisplayHandle display, EGLint interval) { + const std::lock_guard lock(m_mutex); + auto* displayObject = TryGetDisplay(display); + if (!displayObject) { + SetError(EGL_BAD_DISPLAY); + return false; + } + if (!displayObject->Initialized) { + SetError(EGL_NOT_INITIALIZED); + return false; + } + + if (displayObject->Configs.empty()) { + SetError(EGL_BAD_CONFIG); + return false; + } + const auto* cfg = TryGetConfig(displayObject->Configs.front()); + if (!cfg) { + SetError(EGL_BAD_CONFIG); + return false; + } + if (interval < cfg->MinSwapInterval || interval > cfg->MaxSwapInterval) { + SetError(EGL_BAD_PARAMETER); + return false; + } + + displayObject->SwapInterval = interval; + return true; + } + + Bool EGLContext::MakeCurrent(EGLDisplayHandle display, EGLSurfaceHandle draw, EGLSurfaceHandle read, + 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; + const auto threadKey = CurrentThreadKey(); + if (releaseCurrentRequest) { + ReleaseThreadUnlocked(threadKey); + return true; + } + + auto* displayObject = TryGetDisplay(display); + if (!displayObject) { + SetError(EGL_BAD_DISPLAY); + return false; + } + if (!displayObject->Initialized) { + SetError(EGL_NOT_INITIALIZED); + return false; + } + + if (context != nullptr) { + if (!ValidateContextOnDisplay(display, context)) { + SetError(EGL_BAD_CONTEXT); + return false; + } + if (draw == EGL_NO_SURFACE || read == EGL_NO_SURFACE) { + SetError(EGL_BAD_MATCH); + return false; + } + } else { + if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) { + SetError(EGL_BAD_MATCH); + return false; + } + } + + if (draw != EGL_NO_SURFACE && !ValidateSurfaceOnDisplay(display, draw)) { + SetError(EGL_BAD_SURFACE); + return false; + } + if (read != EGL_NO_SURFACE && !ValidateSurfaceOnDisplay(display, read)) { + SetError(EGL_BAD_SURFACE); + return false; + } + + if (context != nullptr) { + auto ownerIt = m_contextOwners.find(context); + if (ownerIt != m_contextOwners.end() && ownerIt->second != threadKey) { + SetError(EGL_BAD_ACCESS); + return false; + } + } + + ReleaseThreadUnlocked(threadKey); + + m_threadCurrents[threadKey] = ThreadCurrentState{ + .Display = display, + .DrawSurface = draw, + .ReadSurface = read, + .Context = context, + }; + if (context != nullptr) { + m_contextOwners[context] = threadKey; + } + return true; + } + + void EGLContext::ReleaseThread() { + const std::lock_guard lock(m_mutex); + ReleaseThreadUnlocked(CurrentThreadKey()); + } + + EGLContext::EGLContextHandle EGLContext::GetCurrentContext() const { + const std::lock_guard lock(m_mutex); + auto currentIt = m_threadCurrents.find(CurrentThreadKey()); + if (currentIt == m_threadCurrents.end()) { + return nullptr; + } + return currentIt->second.Context; + } + + EGLContext::EGLDisplayHandle EGLContext::GetCurrentDisplay() const { + const std::lock_guard lock(m_mutex); + auto currentIt = m_threadCurrents.find(CurrentThreadKey()); + if (currentIt == m_threadCurrents.end()) { + return EGL_NO_DISPLAY; + } + return currentIt->second.Display; + } + + EGLContext::EGLSurfaceHandle EGLContext::GetCurrentSurface(EGLint readdraw) const { + const std::lock_guard lock(m_mutex); + auto currentIt = m_threadCurrents.find(CurrentThreadKey()); + if (currentIt == m_threadCurrents.end()) { + return EGL_NO_SURFACE; + } + if (readdraw == EGL_DRAW) { + return currentIt->second.DrawSurface; + } + if (readdraw == EGL_READ) { + return currentIt->second.ReadSurface; + } + return EGL_NO_SURFACE; + } + + EGLContext::EGLSyncHandle EGLContext::CreateSync(EGLDisplayHandle display, EGLenum type, + const EGLAttrib* attribList) { + const std::lock_guard lock(m_mutex); + if (!IsDisplayInitialized(display)) { + SetError(EGL_NOT_INITIALIZED); + return EGL_NO_SYNC; + } + if (type != EGL_SYNC_FENCE) { + SetError(EGL_BAD_ATTRIBUTE); + return EGL_NO_SYNC; + } + + SyncObject sync = { + .Display = display, + .Type = type, + .Condition = EGL_SYNC_PRIOR_COMMANDS_COMPLETE, + .Status = EGL_SIGNALED, + }; + if (auto value = ParseAttribValue(attribList, EGL_SYNC_CONDITION); value) { + sync.Condition = static_cast(*value); + } + + const auto syncHandle = EncodeHandle(m_nextSyncHandle++); + m_syncs[syncHandle] = sync; + return syncHandle; + } + + Bool EGLContext::DestroySync(EGLDisplayHandle display, EGLSyncHandle sync) { + const std::lock_guard lock(m_mutex); + auto syncIt = m_syncs.find(sync); + if (syncIt == m_syncs.end()) { + SetError(EGL_BAD_PARAMETER); + return false; + } + if (syncIt->second.Display != display) { + SetError(EGL_BAD_MATCH); + return false; + } + m_syncs.erase(syncIt); + return true; + } + + EGLint EGLContext::ClientWaitSync(EGLDisplayHandle display, EGLSyncHandle sync, EGLint flags, + EGLTime timeout) { + const std::lock_guard lock(m_mutex); + (void)flags; + (void)timeout; + + const auto* syncObject = TryGetSync(sync); + if (!syncObject) { + SetError(EGL_BAD_PARAMETER); + return EGL_FALSE; + } + if (syncObject->Display != display) { + SetError(EGL_BAD_MATCH); + return EGL_FALSE; + } + + return EGL_CONDITION_SATISFIED; + } + + Bool EGLContext::GetSyncAttrib(EGLDisplayHandle display, EGLSyncHandle sync, EGLint attribute, + EGLAttrib* value) const { + const std::lock_guard lock(m_mutex); + if (!value) { + const_cast(this)->SetError(EGL_BAD_PARAMETER); + return false; + } + const auto* syncObject = TryGetSync(sync); + if (!syncObject) { + const_cast(this)->SetError(EGL_BAD_PARAMETER); + return false; + } + if (syncObject->Display != display) { + const_cast(this)->SetError(EGL_BAD_MATCH); + return false; + } + + switch (attribute) { + case EGL_SYNC_TYPE: + *value = static_cast(syncObject->Type); + return true; + case EGL_SYNC_STATUS: + *value = static_cast(syncObject->Status); + return true; + case EGL_SYNC_CONDITION: + *value = static_cast(syncObject->Condition); + return true; + default: + const_cast(this)->SetError(EGL_BAD_ATTRIBUTE); + return false; + } + } + + Bool EGLContext::WaitSync(EGLDisplayHandle display, EGLSyncHandle sync, EGLint flags) const { + const std::lock_guard lock(m_mutex); + (void)flags; + const auto* syncObject = TryGetSync(sync); + if (!syncObject) { + const_cast(this)->SetError(EGL_BAD_PARAMETER); + return false; + } + if (syncObject->Display != display) { + const_cast(this)->SetError(EGL_BAD_MATCH); + return false; + } + return true; + } + + EGLContext::EGLImageHandle EGLContext::CreateImage(EGLDisplayHandle display, EGLContextHandle context, + EGLenum target, EGLClientBuffer buffer, + const EGLAttrib* attribList) { + const std::lock_guard lock(m_mutex); + (void)attribList; + if (!IsDisplayInitialized(display)) { + SetError(EGL_NOT_INITIALIZED); + return EGL_NO_IMAGE; + } + if (context != nullptr && !ValidateContextOnDisplay(display, context)) { + SetError(EGL_BAD_CONTEXT); + return EGL_NO_IMAGE; + } + + const auto image = EncodeHandle(m_nextImageHandle++); + m_images[image] = ImageObject{ + .Display = display, + .Context = context, + .Target = target, + .Buffer = buffer, + }; + return image; + } + + Bool EGLContext::DestroyImage(EGLDisplayHandle display, EGLImageHandle image) { + const std::lock_guard lock(m_mutex); + auto imageIt = m_images.find(image); + if (imageIt == m_images.end()) { + SetError(EGL_BAD_PARAMETER); + return false; + } + if (imageIt->second.Display != display) { + SetError(EGL_BAD_MATCH); + return false; + } + m_images.erase(imageIt); + return true; + } + } // namespace EGLState + + EGLState::EGLContext* pEGLContext; + } // namespace MG_State +} // namespace MobileGL diff --git a/MobileGL/MG_State/EGLState/Core.h b/MobileGL/MG_State/EGLState/Core.h new file mode 100644 index 00000000..daf62493 --- /dev/null +++ b/MobileGL/MG_State/EGLState/Core.h @@ -0,0 +1,258 @@ +// MobileGL - MobileGL/MG_State/EGLState/Core.h +// Copyright (c) 2025-2026 MobileGL-Dev +// Licensed under the GNU Lesser General Public License v3.0: +// https://www.gnu.org/licenses/gpl-3.0.txt +// https://www.gnu.org/licenses/lgpl-3.0.txt +// SPDX-License-Identifier: LGPL-3.0-only +// End of Source File Header + +#pragma once +#include +#include + +namespace MobileGL { + namespace MG_State { + namespace EGLState { + class EGLContext { + public: + using EGLDisplayHandle = ::EGLDisplay; + using EGLConfigHandle = ::EGLConfig; + using EGLSurfaceHandle = ::EGLSurface; + using EGLContextHandle = ::EGLContext; + using EGLSyncHandle = ::EGLSync; + using EGLImageHandle = ::EGLImage; + + EGLContext() = default; + + // Error + void SetError(EGLint errorCode); + EGLint ConsumeError(); + + // API + void SetBoundAPI(EGLenum api); + EGLenum GetBoundAPI() const; + + // Display + EGLDisplayHandle GetDisplay(NativeDisplayType nativeDisplay); + EGLDisplayHandle GetPlatformDisplay(EGLenum platform, void* nativeDisplay); + Bool ValidateDisplay(EGLDisplayHandle display) const; + Bool IsDisplayInitialized(EGLDisplayHandle display) const; + Bool InitializeDisplay(EGLDisplayHandle display, EGLint* major, EGLint* minor); + Bool TerminateDisplay(EGLDisplayHandle display); + + // Config + Bool ChooseConfig(EGLDisplayHandle display, const EGLint* attribList, EGLConfigHandle* configs, + EGLint configSize, EGLint* numConfig); + Bool GetConfigs(EGLDisplayHandle display, EGLConfigHandle* configs, EGLint configSize, + EGLint* numConfig); + Bool ValidateConfig(EGLConfigHandle config) const; + Bool ValidateConfigOnDisplay(EGLDisplayHandle display, EGLConfigHandle config) const; + Bool GetConfigAttrib(EGLDisplayHandle display, EGLConfigHandle config, EGLint attribute, + EGLint* value) const; + + // Context + EGLContextHandle CreateContext(EGLDisplayHandle display, EGLConfigHandle config, + EGLContextHandle shareCtx, const EGLint* attribList); + Bool DestroyContext(EGLDisplayHandle display, EGLContextHandle context); + Bool QueryContext(EGLDisplayHandle display, EGLContextHandle context, EGLint attribute, + EGLint* value) const; + Bool ValidateContext(EGLContextHandle context) const; + Bool ValidateContextOnDisplay(EGLDisplayHandle display, EGLContextHandle context) const; + + // Surface + EGLSurfaceHandle CreateWindowSurface(EGLDisplayHandle display, EGLConfigHandle config, + NativeWindowType window, const EGLint* attribList); + EGLSurfaceHandle CreatePbufferSurface(EGLDisplayHandle display, EGLConfigHandle config, + const EGLint* attribList); + EGLSurfaceHandle CreatePixmapSurface(EGLDisplayHandle display, EGLConfigHandle config, + EGLNativePixmapType pixmap, const EGLint* attribList); + EGLSurfaceHandle CreatePbufferFromClientBuffer(EGLDisplayHandle display, EGLenum bufferType, + EGLClientBuffer buffer, EGLConfigHandle config, + const EGLint* attribList); + EGLSurfaceHandle CreatePlatformWindowSurface(EGLDisplayHandle display, EGLConfigHandle config, + void* nativeWindow, const EGLAttrib* attribList); + EGLSurfaceHandle CreatePlatformPixmapSurface(EGLDisplayHandle display, EGLConfigHandle config, + void* nativePixmap, const EGLAttrib* attribList); + Bool DestroySurface(EGLDisplayHandle display, EGLSurfaceHandle surface); + Bool QuerySurface(EGLDisplayHandle display, EGLSurfaceHandle surface, EGLint attribute, + EGLint* value) const; + Bool ValidateSurface(EGLSurfaceHandle surface) const; + Bool ValidateSurfaceOnDisplay(EGLDisplayHandle display, EGLSurfaceHandle surface) const; + Bool SwapInterval(EGLDisplayHandle display, EGLint interval); + + // Current + Bool MakeCurrent(EGLDisplayHandle display, EGLSurfaceHandle draw, EGLSurfaceHandle read, + EGLContextHandle context); + void ReleaseThread(); + EGLContextHandle GetCurrentContext() const; + EGLDisplayHandle GetCurrentDisplay() const; + EGLSurfaceHandle GetCurrentSurface(EGLint readdraw) const; + + // Sync + EGLSyncHandle CreateSync(EGLDisplayHandle display, EGLenum type, const EGLAttrib* attribList); + Bool DestroySync(EGLDisplayHandle display, EGLSyncHandle sync); + EGLint ClientWaitSync(EGLDisplayHandle display, EGLSyncHandle sync, EGLint flags, EGLTime timeout); + Bool GetSyncAttrib(EGLDisplayHandle display, EGLSyncHandle sync, EGLint attribute, + EGLAttrib* value) const; + Bool WaitSync(EGLDisplayHandle display, EGLSyncHandle sync, EGLint flags) const; + + // Image + EGLImageHandle CreateImage(EGLDisplayHandle display, EGLContextHandle context, EGLenum target, + EGLClientBuffer buffer, const EGLAttrib* attribList); + Bool DestroyImage(EGLDisplayHandle display, EGLImageHandle image); + + private: + enum class SurfaceType { + Window, + Pbuffer, + Pixmap, + PbufferFromClientBuffer, + PlatformWindow, + PlatformPixmap + }; + + struct DisplayLookupKey { + Uint64 NativeDisplayKey = 0; + EGLenum Platform = EGL_NONE; + + Bool operator==(const DisplayLookupKey& rhs) const = default; + }; + + struct DisplayLookupHasher { + SizeT operator()(const DisplayLookupKey& key) const; + }; + + struct DisplayObject { + Uint64 NativeDisplayKey = 0; + EGLenum Platform = EGL_NONE; + Bool Initialized = false; + EGLint MajorVersion = 1; + EGLint MinorVersion = 5; + EGLint SwapInterval = 1; + Vector Configs; + }; + + struct ConfigObject { + EGLDisplayHandle Display = EGL_NO_DISPLAY; + EGLint ConfigId = 1; + EGLint RedSize = 8; + EGLint GreenSize = 8; + EGLint BlueSize = 8; + EGLint AlphaSize = 8; + EGLint DepthSize = 24; + EGLint StencilSize = 8; + EGLint SurfaceType = EGL_WINDOW_BIT | EGL_PBUFFER_BIT | EGL_PIXMAP_BIT; + EGLint RenderableType = EGL_OPENGL_BIT | EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT; + EGLint MinSwapInterval = 0; + EGLint MaxSwapInterval = 4; + EGLint NativeVisualId = 0; + }; + + struct ContextObject { + EGLDisplayHandle Display = EGL_NO_DISPLAY; + EGLConfigHandle Config = nullptr; + EGLContextHandle SharedContext = nullptr; + EGLenum ClientAPI = EGL_OPENGL_API; + EGLint ClientVersion = 1; + EGLint MajorVersion = 1; + EGLint MinorVersion = 0; + }; + + struct SurfaceObject { + EGLDisplayHandle Display = EGL_NO_DISPLAY; + EGLConfigHandle Config = nullptr; + SurfaceType Type = SurfaceType::Window; + Uint64 NativeHandleKey = 0; + EGLClientBuffer ClientBuffer = nullptr; + EGLenum BufferType = EGL_NONE; + EGLint Width = 0; + EGLint Height = 0; + EGLint TextureFormat = EGL_NO_TEXTURE; + EGLint TextureTarget = EGL_NO_TEXTURE; + EGLint MipmapLevel = 0; + EGLint MipmapTexture = EGL_FALSE; + EGLint RenderBuffer = EGL_BACK_BUFFER; + EGLint SwapBehavior = EGL_BUFFER_DESTROYED; + }; + + struct SyncObject { + EGLDisplayHandle Display = EGL_NO_DISPLAY; + EGLenum Type = EGL_SYNC_FENCE; + EGLenum Condition = EGL_SYNC_PRIOR_COMMANDS_COMPLETE; + EGLenum Status = EGL_SIGNALED; + }; + + struct ImageObject { + EGLDisplayHandle Display = EGL_NO_DISPLAY; + EGLContextHandle Context = nullptr; + EGLenum Target = EGL_NONE; + EGLClientBuffer Buffer = nullptr; + }; + + struct ThreadCurrentState { + EGLDisplayHandle Display = EGL_NO_DISPLAY; + EGLSurfaceHandle DrawSurface = EGL_NO_SURFACE; + EGLSurfaceHandle ReadSurface = EGL_NO_SURFACE; + EGLContextHandle Context = nullptr; + }; + + template + static HandleType EncodeHandle(Uint64 rawHandle) { + return reinterpret_cast(static_cast(rawHandle)); + } + + template + static Uint64 ToNativeKey(NativeType nativeHandle) { + if constexpr (std::is_pointer_v) { + return static_cast(reinterpret_cast(nativeHandle)); + } else { + return static_cast(nativeHandle); + } + } + + static Optional ParseAttribValue(const EGLint* attribList, EGLint attrib); + static Optional ParseAttribValue(const EGLAttrib* attribList, EGLint attrib); + static std::thread::id CurrentThreadKey(); + + EGLDisplayHandle GetOrCreateDisplay(Uint64 nativeDisplayKey, EGLenum platform); + EGLConfigHandle CreateDefaultConfig(EGLDisplayHandle display); + + DisplayObject* TryGetDisplay(EGLDisplayHandle display); + const DisplayObject* TryGetDisplay(EGLDisplayHandle display) const; + const ConfigObject* TryGetConfig(EGLConfigHandle config) const; + const ContextObject* TryGetContext(EGLContextHandle context) const; + const SurfaceObject* TryGetSurface(EGLSurfaceHandle surface) const; + const SyncObject* TryGetSync(EGLSyncHandle sync) const; + const ImageObject* TryGetImage(EGLImageHandle image) const; + + void ReleaseDisplayObjects(EGLDisplayHandle display); + void ReleaseThreadUnlocked(const std::thread::id& threadKey); + + private: + mutable std::recursive_mutex m_mutex; + + Uint64 m_nextDisplayHandle = 1; + Uint64 m_nextConfigHandle = 1; + Uint64 m_nextSurfaceHandle = 1; + Uint64 m_nextContextHandle = 1; + Uint64 m_nextSyncHandle = 1; + Uint64 m_nextImageHandle = 1; + + UnorderedMap m_displayLookup; + UnorderedMap m_displays; + UnorderedMap m_configs; + UnorderedMap m_surfaces; + UnorderedMap m_contexts; + UnorderedMap m_syncs; + UnorderedMap m_images; + + UnorderedMap m_threadErrors; + UnorderedMap m_threadBoundAPI; + UnorderedMap m_threadCurrents; + UnorderedMap m_contextOwners; + }; + } // namespace EGLState + + extern EGLState::EGLContext* pEGLContext; + } // namespace MG_State +} // namespace MobileGL diff --git a/MobileGL/MG_State/GLState/Core.cpp b/MobileGL/MG_State/GLState/Core.cpp index dc91933a..1681c7eb 100644 --- a/MobileGL/MG_State/GLState/Core.cpp +++ b/MobileGL/MG_State/GLState/Core.cpp @@ -8,12 +8,14 @@ #include "Core.h" #include "MG_State/GLState/RenderbufferState/RenderbufferObject.h" +#include "MG_State/EGLState/Core.h" namespace MobileGL { namespace MG_State { void Init() { MGLOG_D("Initializing MobileGL State..."); pGLContext = new MG_State::GLState::GLContext(); + pEGLContext = new MG_State::EGLState::EGLContext(); } namespace GLState {