From ff41e59282807ff287b85ba65ef04d367c901458 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Sun, 7 Jun 2026 08:21:20 +0800 Subject: [PATCH] [Feat] (EGL): support Linux X11 + EGL --- MobileGL/Includes.h | 1 + MobileGL/MG_Backend/BackendObject.cpp | 48 ++++- MobileGL/MG_Backend/BackendObject.h | 12 +- .../DirectGLES/BackendObject_DirectGLES.cpp | 17 +- .../DirectGLES/BackendObject_DirectGLES.h | 1 + MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 169 +++++++++++++--- MobileGL/MG_Backend/DirectGLES/DirectGLES.h | 5 +- .../BackendObject_DirectVulkan.cpp | 12 +- MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp | 33 +++- .../MG_Impl/GLImpl/Exporting/Definitions.cpp | 147 ++++++++++---- MobileGL/MG_State/EGLState/Core.cpp | 187 +++++++++++++++++- MobileGL/MG_State/EGLState/Core.h | 2 +- 12 files changed, 527 insertions(+), 107 deletions(-) diff --git a/MobileGL/Includes.h b/MobileGL/Includes.h index dd31bad7..9c36ab3d 100644 --- a/MobileGL/Includes.h +++ b/MobileGL/Includes.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/MobileGL/MG_Backend/BackendObject.cpp b/MobileGL/MG_Backend/BackendObject.cpp index d8b93030..1574b33e 100644 --- a/MobileGL/MG_Backend/BackendObject.cpp +++ b/MobileGL/MG_Backend/BackendObject.cpp @@ -53,7 +53,8 @@ namespace MobileGL::MG_Backend { return false; } - if (m_eglWindowSurfaceInitialized && m_windowHandle.Backend == handle.Backend && m_windowHandle.Handle == handle.Handle) { + if (m_eglSurfaceInitialized && m_eglSurfaceKind == SurfaceKind::Window && + m_windowHandle.Backend == handle.Backend && m_windowHandle.Handle == handle.Handle) { return true; } @@ -63,7 +64,35 @@ namespace MobileGL::MG_Backend { return false; } - m_eglWindowSurfaceInitialized = true; + m_eglSurfaceInitialized = true; + m_eglSurfaceKind = SurfaceKind::Window; + m_eglCurrentThreads.clear(); + m_backendCapabilitiesInitialized = false; + return true; + } + + Bool BackendObject::CreateEGLPbufferSurface(EGLint width, EGLint height) { + const std::lock_guard lock(m_eglStateMutex); + if (!m_eglDisplayInitialized) { + MGLOG_E("CreateEGLPbufferSurface failed: EGL display is not initialized"); + return false; + } + if (width <= 0 || height <= 0) { + MGLOG_E("CreateEGLPbufferSurface failed: invalid size %dx%d", width, height); + return false; + } + + if (m_eglSurfaceInitialized && m_eglSurfaceKind == SurfaceKind::Pbuffer) { + return true; + } + + if (!InitPbufferSurface(width, height)) { + MGLOG_E("CreateEGLPbufferSurface failed: backend InitPbufferSurface failed"); + return false; + } + + m_eglSurfaceInitialized = true; + m_eglSurfaceKind = SurfaceKind::Pbuffer; m_eglCurrentThreads.clear(); m_backendCapabilitiesInitialized = false; return true; @@ -81,8 +110,8 @@ namespace MobileGL::MG_Backend { 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"); + 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) { @@ -104,8 +133,9 @@ namespace MobileGL::MG_Backend { void BackendObject::ResetEGLRuntimeState() { const std::lock_guard lock(m_eglStateMutex); - m_eglWindowSurfaceInitialized = false; + m_eglSurfaceInitialized = false; m_backendCapabilitiesInitialized = false; + m_eglSurfaceKind = SurfaceKind::None; m_eglCurrentThreads.clear(); } @@ -119,7 +149,7 @@ namespace MobileGL::MG_Backend { MGLOG_E("SwapEGLBuffers failed: no current context attached"); return false; } - if (!m_eglWindowSurfaceInitialized || draw == EGL_NO_SURFACE) { + if (!m_eglSurfaceInitialized || draw == EGL_NO_SURFACE) { MGLOG_E("SwapEGLBuffers failed: invalid draw surface"); return false; } @@ -138,4 +168,10 @@ namespace MobileGL::MG_Backend { const std::lock_guard lock(m_eglStateMutex); m_windowHandle = handle; } + + Bool BackendObject::InitPbufferSurface(EGLint width, EGLint height) { + (void)width; + (void)height; + return false; + } } // namespace MobileGL::MG_Backend diff --git a/MobileGL/MG_Backend/BackendObject.h b/MobileGL/MG_Backend/BackendObject.h index 2f2354b6..27ec3a10 100644 --- a/MobileGL/MG_Backend/BackendObject.h +++ b/MobileGL/MG_Backend/BackendObject.h @@ -94,6 +94,7 @@ namespace MobileGL { enum class WindowBackend { Android, + X11, // TODO: X11, Wayland, Windows, macOS, etc. WindowBackendCount, Unknown = -1 @@ -114,6 +115,7 @@ namespace MobileGL { virtual Bool InitializeEGLDisplay(EGLDisplay dpy, EGLint* major, EGLint* minor); virtual Bool CreateEGLWindowSurface(const WindowHandle& handle); + virtual Bool CreateEGLPbufferSurface(EGLint width, EGLint height); virtual Bool MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx); virtual Bool SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw); @@ -126,14 +128,22 @@ namespace MobileGL { virtual BackendType GetBackendType() const = 0; protected: + enum class SurfaceKind { + None, + Window, + Pbuffer + }; + void ResetEGLRuntimeState(); + virtual Bool InitPbufferSurface(EGLint width, EGLint height); 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_eglSurfaceInitialized = false; Bool m_backendCapabilitiesInitialized = false; + SurfaceKind m_eglSurfaceKind = SurfaceKind::None; UnorderedMap m_eglCurrentThreads; }; } // namespace MG_Backend diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp index 039b8ea1..b8aad546 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp @@ -78,18 +78,18 @@ namespace MobileGL::MG_Backend::DirectGLES { return false; } - if (handle.Backend != WindowBackend::Android || !handle.Handle) { - MGLOG_E("DirectGLES backend only supports Android native windows"); + if ((handle.Backend != WindowBackend::Android && handle.Backend != WindowBackend::X11) || !handle.Handle) { + MGLOG_E("DirectGLES backend only supports Android and X11 native windows"); return false; } - const Bool sameHandle = - m_eglWindowSurfaceInitialized && m_windowHandle.Backend == handle.Backend && m_windowHandle.Handle == handle.Handle; + const Bool sameHandle = m_eglSurfaceInitialized && m_eglSurfaceKind == SurfaceKind::Window && + m_windowHandle.Backend == handle.Backend && m_windowHandle.Handle == handle.Handle; if (sameHandle) { return true; } - if (m_eglWindowSurfaceInitialized) { + if (m_eglSurfaceInitialized) { DestroyEGLContext(); ResetEGLRuntimeState(); } @@ -97,6 +97,10 @@ namespace MobileGL::MG_Backend::DirectGLES { return BackendObject::CreateEGLWindowSurface(handle); } + Bool BackendObject_DirectGLES::InitPbufferSurface(EGLint width, EGLint height) { + return DirectGLES::InitPbufferSurface(width, height); + } + 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); @@ -121,7 +125,8 @@ namespace MobileGL::MG_Backend::DirectGLES { .Extensions = {V_OpenGL30, V_OpenGL31, V_OpenGL32, // OpenGL Extensions V_OpenGL33, E_GL_ARB_draw_buffers_blend, E_GL_ARB_compute_shader, E_GL_ARB_shader_storage_buffer_object, E_GL_ARB_shader_image_load_store, - E_GL_ARB_program_interface_query}, + E_GL_ARB_program_interface_query, E_GL_ARB_framebuffer_object, + E_GL_EXT_framebuffer_object}, .IsCompatibilityProfile = false // Is Compatibility Profile }, .StaticBackendCapability = {.AllowVSOnlyPrograms = false} // Backend Capability diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h index 84b0bc49..70b3b55a 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h @@ -35,6 +35,7 @@ namespace MobileGL::MG_Backend::DirectGLES { private: void UpdateDynamicBackendParameters(); + Bool InitPbufferSurface(EGLint width, EGLint height) override; Bool m_initialized = false; MG_External::EGLFunctionsTable m_EGLFunctions; diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 2d9b469f..c379f2c1 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -134,7 +134,7 @@ namespace MobileGL::MG_Backend::DirectGLES { const auto start = std::min(range.start, obj->GetSize()); const auto end = std::min(range.end, obj->GetSize()); g_GLESFuncs.glBindBufferRange(glTarget, static_cast(i), backendBufferId, - static_cast(start), static_cast(end - start)); + static_cast(start), static_cast(end - start)); } } } @@ -1454,8 +1454,8 @@ namespace MobileGL::MG_Backend::DirectGLES { return g_GLESFuncs.glGetProgramResourceIndex(backendProgramId, programInterface, name); } - void GetProgramResourceName(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, - GLsizei* length, GLchar* name) { + void GetProgramResourceName(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei* length, + GLchar* name) { GLuint backendProgramId = GetBackendProgramId(program); if (!backendProgramId) return; g_GLESFuncs.glGetProgramResourceName(backendProgramId, programInterface, index, bufSize, length, name); @@ -1465,8 +1465,8 @@ namespace MobileGL::MG_Backend::DirectGLES { const GLenum* props, GLsizei bufSize, GLsizei* length, GLint* params) { GLuint backendProgramId = GetBackendProgramId(program); if (!backendProgramId) return; - g_GLESFuncs.glGetProgramResourceiv(backendProgramId, programInterface, index, propCount, props, bufSize, - length, params); + g_GLESFuncs.glGetProgramResourceiv(backendProgramId, programInterface, index, propCount, props, bufSize, length, + params); } GLint GetProgramResourceLocation(GLuint program, GLenum programInterface, const GLchar* name) { @@ -1858,9 +1858,117 @@ namespace MobileGL::MG_Backend::DirectGLES { static EGLContext g_Context = EGL_NO_CONTEXT; static EGLSurface g_Surface = EGL_NO_SURFACE; static EGLConfig g_Config = nullptr; - Bool InitWindowSurface(NativeWindowType window) { - // TODO: handle custom EGL paramters - if (!window) return false; + + static EGLint QueryDefaultX11VisualId() { +#if defined(__linux__) && !defined(__ANDROID__) + const char* displayName = std::getenv("DISPLAY"); + if (!displayName) { + return 0; + } + + void* x11Lib = dlopen("libX11.so.6", RTLD_LOCAL | RTLD_NOW); + if (!x11Lib) { + x11Lib = dlopen("libX11.so", RTLD_LOCAL | RTLD_NOW); + } + if (!x11Lib) { + return 0; + } + + using XOpenDisplayFn = void* (*)(const char*); + using XDefaultScreenFn = int (*)(void*); + using XDefaultVisualFn = void* (*)(void*, int); + using XVisualIDFromVisualFn = unsigned long (*)(void*); + using XCloseDisplayFn = int (*)(void*); + + auto* xOpenDisplay = reinterpret_cast(dlsym(x11Lib, "XOpenDisplay")); + auto* xDefaultScreen = reinterpret_cast(dlsym(x11Lib, "XDefaultScreen")); + auto* xDefaultVisual = reinterpret_cast(dlsym(x11Lib, "XDefaultVisual")); + auto* xVisualIDFromVisual = reinterpret_cast(dlsym(x11Lib, "XVisualIDFromVisual")); + auto* xCloseDisplay = reinterpret_cast(dlsym(x11Lib, "XCloseDisplay")); + if (!xOpenDisplay || !xDefaultScreen || !xDefaultVisual || !xVisualIDFromVisual || !xCloseDisplay) { + dlclose(x11Lib); + return 0; + } + + void* display = xOpenDisplay(displayName); + if (!display) { + dlclose(x11Lib); + return 0; + } + const int screen = xDefaultScreen(display); + void* visual = xDefaultVisual(display, screen); + const auto visualId = visual ? static_cast(xVisualIDFromVisual(visual)) : 0; + xCloseDisplay(display); + dlclose(x11Lib); + return visualId; +#else + return 0; +#endif + } + + static Bool GetConfigAttrib(EGLConfig config, EGLint attr, EGLint& value) { + return g_EGLFuncs.eglGetConfigAttrib && g_EGLFuncs.eglGetConfigAttrib(g_Display, config, attr, &value); + } + + static Bool ConfigSupports(EGLConfig config, EGLint surfaceBit) { + EGLint surfaceType = 0; + EGLint renderableType = 0; + if (!GetConfigAttrib(config, EGL_SURFACE_TYPE, surfaceType)) { + return false; + } + if (!GetConfigAttrib(config, EGL_RENDERABLE_TYPE, renderableType)) { + return false; + } + return (surfaceType & surfaceBit) && (renderableType & EGL_OPENGL_ES3_BIT); + } + + static Bool ChooseConfigForSurface(EGLint surfaceBit, EGLConfig& outConfig) { + const EGLint configAttribs[] = {EGL_SURFACE_TYPE, surfaceBit, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, + EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 24, EGL_STENCIL_SIZE, 8, + EGL_NONE}; + + EGLint numConfigs = 0; + if (!g_EGLFuncs.eglChooseConfig(g_Display, configAttribs, nullptr, 0, &numConfigs) || numConfigs == 0) { + return false; + } + + Vector configs(static_cast(numConfigs)); + if (!g_EGLFuncs.eglChooseConfig(g_Display, configAttribs, configs.data(), numConfigs, &numConfigs) || + numConfigs == 0) { + return false; + } + configs.resize(static_cast(numConfigs)); + + if (surfaceBit == EGL_WINDOW_BIT) { + const EGLint defaultVisualId = QueryDefaultX11VisualId(); + if (defaultVisualId != 0) { + for (const auto config : configs) { + EGLint nativeVisualId = 0; + if (ConfigSupports(config, surfaceBit) && + GetConfigAttrib(config, EGL_NATIVE_VISUAL_ID, nativeVisualId) && + nativeVisualId == defaultVisualId) { + outConfig = config; + return true; + } + } + } + } + + for (const auto config : configs) { + if (ConfigSupports(config, surfaceBit)) { + outConfig = config; + return true; + } + } + + outConfig = configs.front(); + return true; + } + + static Bool InitDisplayAndContext(EGLint surfaceBit) { + DestroyEGLContext(); g_Display = g_EGLFuncs.eglGetDisplay(EGL_DEFAULT_DISPLAY); if (g_Display == EGL_NO_DISPLAY) return false; @@ -1868,32 +1976,18 @@ namespace MobileGL::MG_Backend::DirectGLES { if (!g_EGLFuncs.eglInitialize(g_Display, nullptr, nullptr)) return false; g_EGLFuncs.eglBindAPI(EGL_OPENGL_ES_API); - const EGLint configAttribs[] = {EGL_SURFACE_TYPE, - EGL_WINDOW_BIT, - EGL_RENDERABLE_TYPE, - EGL_OPENGL_ES3_BIT, - EGL_RED_SIZE, - 8, - EGL_GREEN_SIZE, - 8, - EGL_BLUE_SIZE, - 8, - EGL_ALPHA_SIZE, - 8, - EGL_DEPTH_SIZE, - 24, - EGL_STENCIL_SIZE, - 8, - EGL_NONE}; - - EGLint numConfigs = 0; - if (!g_EGLFuncs.eglChooseConfig(g_Display, configAttribs, &g_Config, 1, &numConfigs) || numConfigs == 0) - return false; + if (!ChooseConfigForSurface(surfaceBit, g_Config)) return false; const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; g_Context = g_EGLFuncs.eglCreateContext(g_Display, g_Config, EGL_NO_CONTEXT, contextAttribs); - if (g_Context == EGL_NO_CONTEXT) return false; + return g_Context != EGL_NO_CONTEXT; + } + + Bool InitWindowSurface(NativeWindowType window) { + if (!window) return false; + + if (!InitDisplayAndContext(EGL_WINDOW_BIT)) return false; g_Surface = g_EGLFuncs.eglCreateWindowSurface(g_Display, g_Config, window, nullptr); if (g_Surface == EGL_NO_SURFACE) return false; @@ -1905,6 +1999,21 @@ namespace MobileGL::MG_Backend::DirectGLES { return true; } + Bool InitPbufferSurface(EGLint width, EGLint height) { + if (width <= 0 || height <= 0) return false; + if (!InitDisplayAndContext(EGL_PBUFFER_BIT)) return false; + + const EGLint surfaceAttribs[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE}; + 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; + + 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; + } + void Present() { if (g_Display != EGL_NO_DISPLAY && g_Surface != EGL_NO_SURFACE) { g_EGLFuncs.eglSwapBuffers(g_Display, g_Surface); diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.h b/MobileGL/MG_Backend/DirectGLES/DirectGLES.h index 25aac7f8..271689d6 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.h +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.h @@ -67,14 +67,15 @@ namespace MobileGL::MG_Backend::DirectGLES { void GetProgramiv(GLuint program, GLenum pname, GLint* params); void GetProgramInterfaceiv(GLuint program, GLenum programInterface, GLenum pname, GLint* params); GLuint GetProgramResourceIndex(GLuint program, GLenum programInterface, const GLchar* name); - void GetProgramResourceName(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, - GLsizei* length, GLchar* name); + void GetProgramResourceName(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei* length, + GLchar* name); void GetProgramResourceiv(GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum* props, GLsizei bufSize, GLsizei* length, GLint* params); GLint GetProgramResourceLocation(GLuint program, GLenum programInterface, const GLchar* name); GLint GetProgramResourceLocationIndex(GLuint program, GLenum programInterface, const GLchar* name); void ShaderStorageBlockBinding(GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); Bool InitWindowSurface(NativeWindowType window); + Bool InitPbufferSurface(EGLint width, EGLint height); void Present(); void SetEGLFuncsTable(const MG_External::EGLFunctionsTable& eglFuncs); void SetGLESFuncsTable(const MG_External::GLESFunctionsTable& glesFuncs); diff --git a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp index edc53040..1a2c4d2b 100644 --- a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp @@ -71,13 +71,13 @@ namespace MobileGL::MG_Backend::DirectVulkan { return false; } - const Bool sameHandle = - m_eglWindowSurfaceInitialized && m_windowHandle.Backend == handle.Backend && m_windowHandle.Handle == handle.Handle; + const Bool sameHandle = m_eglSurfaceInitialized && m_eglSurfaceKind == SurfaceKind::Window && + m_windowHandle.Backend == handle.Backend && m_windowHandle.Handle == handle.Handle; if (sameHandle) { return true; } - if (m_eglWindowSurfaceInitialized || pVulkanRenderer) { + if (m_eglSurfaceInitialized || pVulkanRenderer) { pVulkanRenderer.reset(); ResetEGLRuntimeState(); } @@ -116,10 +116,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { .TargetGLVersion = {3, 3, 0}, // Target OpenGL Version .TargetGLSLVersion = {4, 6, 0}, // Target Shading Language Version .Extensions = {V_OpenGL30, V_OpenGL31, V_OpenGL32, // OpenGL Extensions - V_OpenGL33, E_GL_ARB_draw_buffers_blend, - E_GL_ARB_compute_shader, - E_GL_ARB_shader_storage_buffer_object, - E_GL_ARB_shader_image_load_store, + V_OpenGL33, E_GL_ARB_draw_buffers_blend, E_GL_ARB_compute_shader, + E_GL_ARB_shader_storage_buffer_object, E_GL_ARB_shader_image_load_store, E_GL_ARB_program_interface_query}, .IsCompatibilityProfile = false // Is Compatibility Profile }, diff --git a/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp b/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp index 20aa6d1c..1fc03383 100644 --- a/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp +++ b/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp @@ -34,11 +34,25 @@ namespace MobileGL::MG_Impl::EGLImpl { MG_Backend::WindowBackend DetectWindowBackend() { #if defined(ANDROID) || defined(__ANDROID__) return MG_Backend::WindowBackend::Android; +#elif defined(__linux__) + return MG_Backend::WindowBackend::X11; #else return MG_Backend::WindowBackend::Unknown; #endif } + EGLint GetAttribValue(const EGLint* attribList, EGLint attrib, EGLint defaultValue) { + if (!attribList) { + return defaultValue; + } + for (SizeT i = 0; attribList[i] != EGL_NONE; i += 2) { + if (attribList[i] == attrib) { + return attribList[i + 1]; + } + } + return defaultValue; + } + template Bool IsNullNativeHandle(NativeType nativeHandle) { if constexpr (std::is_pointer_v) { @@ -330,7 +344,24 @@ namespace MobileGL::MG_Impl::EGLImpl { if (!state) { return EGL_NO_SURFACE; } - return state->CreatePbufferSurface(dpy, config, attrib_list); + const EGLint width = GetAttribValue(attrib_list, EGL_WIDTH, 1); + const EGLint height = GetAttribValue(attrib_list, EGL_HEIGHT, 1); + EGLSurface surface = state->CreatePbufferSurface(dpy, config, attrib_list); + if (surface == EGL_NO_SURFACE) { + return EGL_NO_SURFACE; + } + + auto* backendObject = GetBackendObject(state); + if (!backendObject) { + return EGL_NO_SURFACE; + } + if (!backendObject->CreateEGLPbufferSurface(width, height)) { + state->DestroySurface(dpy, surface); + state->SetError(EGL_BAD_ALLOC); + return EGL_NO_SURFACE; + } + + return surface; } EGLBoolean BindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) { diff --git a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp index dbdf9294..76a896a6 100644 --- a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp +++ b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp @@ -17,42 +17,40 @@ #include "../Framebuffer/GL_Framebuffer.h" #include "../VertexArray/GL_VertexArray.h" -#define DECLARE_GL_FUNCTION_STUB_HEAD(type,name,...) \ -MOBILEGL_GL_API type gl##name(__VA_ARGS__) { +#define DECLARE_GL_FUNCTION_STUB_HEAD(type, name, ...) MOBILEGL_GL_API type gl##name(__VA_ARGS__) { -#define DECLARE_GL_FUNCTION_STUB_END(type,name,...) \ - MGLOG_W("Stub function: %s(...)", __FUNCTION__); \ - return (type)1; \ -} +#define DECLARE_GL_FUNCTION_STUB_END(type, name, ...) \ + MGLOG_W("Stub function: %s(...)", __FUNCTION__); \ + return (type)1; \ + } -#define DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(type,name,...) \ - MGLOG_W("Stub function: %s(...)", __FUNCTION__); \ -} +#define DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(type, name, ...) \ + MGLOG_W("Stub function: %s(...)", __FUNCTION__); \ + } -#define DECLARE_GL_FUNCTION_HEAD(type,name,...) \ -MOBILEGL_GL_API type gl##name(__VA_ARGS__) { +#define DECLARE_GL_FUNCTION_HEAD(type, name, ...) MOBILEGL_GL_API type gl##name(__VA_ARGS__) { #ifdef TRACY_ENABLE - #define DECLARE_GL_FUNCTION_END(type,name,...) \ - ZoneScopedC(TRACY_ZONECOLOR_ENTRY); \ - MGLOG_D("Implementing function: %s(...)", __FUNCTION__); \ - return MobileGL::MG_Impl::GLImpl::name(__VA_ARGS__); \ +#define DECLARE_GL_FUNCTION_END(type, name, ...) \ + ZoneScopedC(TRACY_ZONECOLOR_ENTRY); \ + MGLOG_D("Implementing function: %s(...)", __FUNCTION__); \ + return MobileGL::MG_Impl::GLImpl::name(__VA_ARGS__); \ } - #define DECLARE_GL_FUNCTION_END_NO_RETURN(type,name,...) \ - ZoneScopedC(TRACY_ZONECOLOR_ENTRY); \ - MGLOG_D("Implementing function: %s(...)", __FUNCTION__); \ - MobileGL::MG_Impl::GLImpl::name(__VA_ARGS__); \ +#define DECLARE_GL_FUNCTION_END_NO_RETURN(type, name, ...) \ + ZoneScopedC(TRACY_ZONECOLOR_ENTRY); \ + MGLOG_D("Implementing function: %s(...)", __FUNCTION__); \ + MobileGL::MG_Impl::GLImpl::name(__VA_ARGS__); \ } #else - #define DECLARE_GL_FUNCTION_END(type,name,...) \ - MGLOG_D("Implementing function: %s(...)", __FUNCTION__); \ - return MobileGL::MG_Impl::GLImpl::name(__VA_ARGS__); \ +#define DECLARE_GL_FUNCTION_END(type, name, ...) \ + MGLOG_D("Implementing function: %s(...)", __FUNCTION__); \ + return MobileGL::MG_Impl::GLImpl::name(__VA_ARGS__); \ } - #define DECLARE_GL_FUNCTION_END_NO_RETURN(type,name,...) \ - MGLOG_D("Implementing function: %s(...)", __FUNCTION__); \ - MobileGL::MG_Impl::GLImpl::name(__VA_ARGS__); \ +#define DECLARE_GL_FUNCTION_END_NO_RETURN(type, name, ...) \ + MGLOG_D("Implementing function: %s(...)", __FUNCTION__); \ + MobileGL::MG_Impl::GLImpl::name(__VA_ARGS__); \ } #endif @@ -2880,6 +2878,69 @@ MOBILEGL_GL_API void glGetFramebufferParameterivEXT(GLenum target, GLenum pname, glGetFramebufferParameteriv(target, pname, params); } +MOBILEGL_GL_API void glBindFramebufferEXT(GLenum target, GLuint framebuffer) { + glBindFramebuffer(target, framebuffer); +} + +MOBILEGL_GL_API void glBindRenderbufferEXT(GLenum target, GLuint renderbuffer) { + glBindRenderbuffer(target, renderbuffer); +} + +MOBILEGL_GL_API GLenum glCheckFramebufferStatusEXT(GLenum target) { + return glCheckFramebufferStatus(target); +} + +MOBILEGL_GL_API void glDeleteFramebuffersEXT(GLsizei n, const GLuint* framebuffers) { + glDeleteFramebuffers(n, framebuffers); +} + +MOBILEGL_GL_API void glDeleteRenderbuffersEXT(GLsizei n, const GLuint* renderbuffers) { + glDeleteRenderbuffers(n, renderbuffers); +} + +MOBILEGL_GL_API void glFramebufferRenderbufferEXT(GLenum target, GLenum attachment, GLenum renderbuffertarget, + GLuint renderbuffer) { + glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer); +} + +MOBILEGL_GL_API void glFramebufferTexture2DEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, + GLint level) { + glFramebufferTexture2D(target, attachment, textarget, texture, level); +} + +MOBILEGL_GL_API void glGenFramebuffersEXT(GLsizei n, GLuint* framebuffers) { + glGenFramebuffers(n, framebuffers); +} + +MOBILEGL_GL_API void glGenRenderbuffersEXT(GLsizei n, GLuint* renderbuffers) { + glGenRenderbuffers(n, renderbuffers); +} + +MOBILEGL_GL_API void glGenerateMipmapEXT(GLenum target) { + glGenerateMipmap(target); +} + +MOBILEGL_GL_API void glGetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, GLenum pname, + GLint* params) { + glGetFramebufferAttachmentParameteriv(target, attachment, pname, params); +} + +MOBILEGL_GL_API void glGetRenderbufferParameterivEXT(GLenum target, GLenum pname, GLint* params) { + glGetRenderbufferParameteriv(target, pname, params); +} + +MOBILEGL_GL_API GLboolean glIsFramebufferEXT(GLuint framebuffer) { + return glIsFramebuffer(framebuffer); +} + +MOBILEGL_GL_API GLboolean glIsRenderbufferEXT(GLuint renderbuffer) { + return glIsRenderbuffer(renderbuffer); +} + +MOBILEGL_GL_API void glRenderbufferStorageEXT(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) { + glRenderbufferStorage(target, internalformat, width, height); +} + MOBILEGL_GL_API GLenum glGetGraphicsResetStatusARB(void) { return glGetGraphicsResetStatus(); } @@ -3025,48 +3086,48 @@ MOBILEGL_GL_API void glProgramUniform4uivEXT(GLuint program, GLint location, GLs glProgramUniform4uiv(program, location, count, value); } -MOBILEGL_GL_API void glProgramUniformMatrix2fvEXT(GLuint program, GLint location, GLsizei count, - GLboolean transpose, const GLfloat* value) { +MOBILEGL_GL_API void glProgramUniformMatrix2fvEXT(GLuint program, GLint location, GLsizei count, GLboolean transpose, + const GLfloat* value) { glProgramUniformMatrix2fv(program, location, count, transpose, value); } -MOBILEGL_GL_API void glProgramUniformMatrix2x3fvEXT(GLuint program, GLint location, GLsizei count, - GLboolean transpose, const GLfloat* value) { +MOBILEGL_GL_API void glProgramUniformMatrix2x3fvEXT(GLuint program, GLint location, GLsizei count, GLboolean transpose, + const GLfloat* value) { glProgramUniformMatrix2x3fv(program, location, count, transpose, value); } -MOBILEGL_GL_API void glProgramUniformMatrix2x4fvEXT(GLuint program, GLint location, GLsizei count, - GLboolean transpose, const GLfloat* value) { +MOBILEGL_GL_API void glProgramUniformMatrix2x4fvEXT(GLuint program, GLint location, GLsizei count, GLboolean transpose, + const GLfloat* value) { glProgramUniformMatrix2x4fv(program, location, count, transpose, value); } -MOBILEGL_GL_API void glProgramUniformMatrix3fvEXT(GLuint program, GLint location, GLsizei count, - GLboolean transpose, const GLfloat* value) { +MOBILEGL_GL_API void glProgramUniformMatrix3fvEXT(GLuint program, GLint location, GLsizei count, GLboolean transpose, + const GLfloat* value) { glProgramUniformMatrix3fv(program, location, count, transpose, value); } -MOBILEGL_GL_API void glProgramUniformMatrix3x2fvEXT(GLuint program, GLint location, GLsizei count, - GLboolean transpose, const GLfloat* value) { +MOBILEGL_GL_API void glProgramUniformMatrix3x2fvEXT(GLuint program, GLint location, GLsizei count, GLboolean transpose, + const GLfloat* value) { glProgramUniformMatrix3x2fv(program, location, count, transpose, value); } -MOBILEGL_GL_API void glProgramUniformMatrix3x4fvEXT(GLuint program, GLint location, GLsizei count, - GLboolean transpose, const GLfloat* value) { +MOBILEGL_GL_API void glProgramUniformMatrix3x4fvEXT(GLuint program, GLint location, GLsizei count, GLboolean transpose, + const GLfloat* value) { glProgramUniformMatrix3x4fv(program, location, count, transpose, value); } -MOBILEGL_GL_API void glProgramUniformMatrix4fvEXT(GLuint program, GLint location, GLsizei count, - GLboolean transpose, const GLfloat* value) { +MOBILEGL_GL_API void glProgramUniformMatrix4fvEXT(GLuint program, GLint location, GLsizei count, GLboolean transpose, + const GLfloat* value) { glProgramUniformMatrix4fv(program, location, count, transpose, value); } -MOBILEGL_GL_API void glProgramUniformMatrix4x2fvEXT(GLuint program, GLint location, GLsizei count, - GLboolean transpose, const GLfloat* value) { +MOBILEGL_GL_API void glProgramUniformMatrix4x2fvEXT(GLuint program, GLint location, GLsizei count, GLboolean transpose, + const GLfloat* value) { glProgramUniformMatrix4x2fv(program, location, count, transpose, value); } -MOBILEGL_GL_API void glProgramUniformMatrix4x3fvEXT(GLuint program, GLint location, GLsizei count, - GLboolean transpose, const GLfloat* value) { +MOBILEGL_GL_API void glProgramUniformMatrix4x3fvEXT(GLuint program, GLint location, GLsizei count, GLboolean transpose, + const GLfloat* value) { glProgramUniformMatrix4x3fv(program, location, count, transpose, value); } diff --git a/MobileGL/MG_State/EGLState/Core.cpp b/MobileGL/MG_State/EGLState/Core.cpp index f0488b4e..c9ef85d6 100644 --- a/MobileGL/MG_State/EGLState/Core.cpp +++ b/MobileGL/MG_State/EGLState/Core.cpp @@ -14,6 +14,7 @@ namespace MobileGL { namespace { constexpr EGLint EGL_DISPLAY_MAJOR_VERSION = 1; constexpr EGLint EGL_DISPLAY_MINOR_VERSION = 5; + constexpr EGLint DEFAULT_MAX_PBUFFER_SIZE = 16384; template Optional ParseAttribValue(const AttrType* attribList, EGLint attrib) { @@ -27,6 +28,55 @@ namespace MobileGL { } return Nullopt; } + + EGLint QueryDefaultX11VisualId() { +#if defined(__linux__) && !defined(__ANDROID__) + const char* displayName = std::getenv("DISPLAY"); + if (!displayName) { + return 0; + } + + void* x11Lib = dlopen("libX11.so.6", RTLD_LOCAL | RTLD_NOW); + if (!x11Lib) { + x11Lib = dlopen("libX11.so", RTLD_LOCAL | RTLD_NOW); + } + if (!x11Lib) { + return 0; + } + + using XOpenDisplayFn = void* (*)(const char*); + using XDefaultScreenFn = int (*)(void*); + using XDefaultVisualFn = void* (*)(void*, int); + using XVisualIDFromVisualFn = unsigned long (*)(void*); + using XCloseDisplayFn = int (*)(void*); + + auto* xOpenDisplay = reinterpret_cast(dlsym(x11Lib, "XOpenDisplay")); + auto* xDefaultScreen = reinterpret_cast(dlsym(x11Lib, "XDefaultScreen")); + auto* xDefaultVisual = reinterpret_cast(dlsym(x11Lib, "XDefaultVisual")); + auto* xVisualIDFromVisual = + reinterpret_cast(dlsym(x11Lib, "XVisualIDFromVisual")); + auto* xCloseDisplay = reinterpret_cast(dlsym(x11Lib, "XCloseDisplay")); + if (!xOpenDisplay || !xDefaultScreen || !xDefaultVisual || !xVisualIDFromVisual || !xCloseDisplay) { + dlclose(x11Lib); + return 0; + } + + void* display = xOpenDisplay(displayName); + if (!display) { + dlclose(x11Lib); + return 0; + } + + const int screen = xDefaultScreen(display); + void* visual = xDefaultVisual(display, screen); + const auto visualId = visual ? static_cast(xVisualIDFromVisual(visual)) : 0; + xCloseDisplay(display); + dlclose(x11Lib); + return visualId; +#else + return 0; +#endif + } } // namespace Bool EGLContext::DisplayLookupKey::operator==(const DisplayLookupKey& rhs) const { @@ -99,30 +149,31 @@ namespace MobileGL { .SwapInterval = 1, }; - const auto config = CreateDefaultConfig(display); - displayObject.Configs.push_back(config); + displayObject.Configs.push_back(CreateDefaultConfig(display, 1, 0)); + displayObject.Configs.push_back(CreateDefaultConfig(display, 2, 8)); m_displays[display] = displayObject; m_displayLookup[key] = display; return display; } - EGLContext::EGLConfigHandle EGLContext::CreateDefaultConfig(EGLDisplayHandle display) { + EGLContext::EGLConfigHandle EGLContext::CreateDefaultConfig(EGLDisplayHandle display, EGLint configId, + EGLint stencilSize) { const auto config = EncodeHandle(m_nextConfigHandle++); ConfigObject cfg = { .Display = display, - .ConfigId = 1, + .ConfigId = configId, .RedSize = 8, .GreenSize = 8, .BlueSize = 8, .AlphaSize = 8, .DepthSize = 24, - .StencilSize = 8, + .StencilSize = stencilSize, .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, + .NativeVisualId = QueryDefaultX11VisualId(), }; #if defined(ANDROID) || defined(__ANDROID__) cfg.NativeVisualId = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; @@ -294,7 +345,6 @@ namespace MobileGL { 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); @@ -309,14 +359,84 @@ namespace MobileGL { return false; } - *numConfig = static_cast(displayObject->Configs.size()); + Vector matchedConfigs; + for (const auto config : displayObject->Configs) { + const auto* cfg = TryGetConfig(config); + if (!cfg) { + continue; + } + + Bool matches = true; + if (attribList) { + for (SizeT i = 0; attribList[i] != EGL_NONE; i += 2) { + const EGLint attr = attribList[i]; + const EGLint requested = attribList[i + 1]; + if (requested == EGL_DONT_CARE) { + continue; + } + + switch (attr) { + case EGL_CONFIG_ID: + matches = cfg->ConfigId == requested; + break; + case EGL_RED_SIZE: + matches = cfg->RedSize >= requested; + break; + case EGL_GREEN_SIZE: + matches = cfg->GreenSize >= requested; + break; + case EGL_BLUE_SIZE: + matches = cfg->BlueSize >= requested; + break; + case EGL_ALPHA_SIZE: + matches = cfg->AlphaSize >= requested; + break; + case EGL_BUFFER_SIZE: + matches = (cfg->RedSize + cfg->GreenSize + cfg->BlueSize + cfg->AlphaSize) >= requested; + break; + case EGL_DEPTH_SIZE: + matches = cfg->DepthSize >= requested; + break; + case EGL_STENCIL_SIZE: + matches = cfg->StencilSize >= requested; + break; + case EGL_SURFACE_TYPE: + matches = (cfg->SurfaceType & requested) == requested; + break; + case EGL_RENDERABLE_TYPE: + case EGL_CONFORMANT: + matches = (cfg->RenderableType & requested) == requested; + break; + case EGL_SAMPLE_BUFFERS: + case EGL_SAMPLES: + matches = requested == 0; + break; + case EGL_NATIVE_VISUAL_ID: + matches = cfg->NativeVisualId == requested; + break; + default: + break; + } + + if (!matches) { + break; + } + } + } + + if (matches) { + matchedConfigs.push_back(config); + } + } + + *numConfig = static_cast(matchedConfigs.size()); if (!configs || configSize <= 0) { return true; } - const SizeT copyCount = std::min(static_cast(configSize), displayObject->Configs.size()); + const SizeT copyCount = std::min(static_cast(configSize), matchedConfigs.size()); for (SizeT i = 0; i < copyCount; ++i) { - configs[i] = displayObject->Configs[i]; + configs[i] = matchedConfigs[i]; } return true; } @@ -356,9 +476,38 @@ namespace MobileGL { } switch (attribute) { + case EGL_BUFFER_SIZE: + *value = cfg->RedSize + cfg->GreenSize + cfg->BlueSize + cfg->AlphaSize; + return true; + case EGL_ALPHA_MASK_SIZE: + *value = 0; + return true; + case EGL_BIND_TO_TEXTURE_RGB: + case EGL_BIND_TO_TEXTURE_RGBA: + *value = EGL_FALSE; + return true; + case EGL_COLOR_BUFFER_TYPE: + *value = EGL_RGB_BUFFER; + return true; + case EGL_CONFIG_CAVEAT: + *value = EGL_NONE; + return true; case EGL_CONFIG_ID: *value = cfg->ConfigId; return true; + case EGL_LEVEL: + *value = 0; + return true; + case EGL_LUMINANCE_SIZE: + *value = 0; + return true; + case EGL_MAX_PBUFFER_WIDTH: + case EGL_MAX_PBUFFER_HEIGHT: + *value = DEFAULT_MAX_PBUFFER_SIZE; + return true; + case EGL_MAX_PBUFFER_PIXELS: + *value = DEFAULT_MAX_PBUFFER_SIZE * DEFAULT_MAX_PBUFFER_SIZE; + return true; case EGL_RED_SIZE: *value = cfg->RedSize; return true; @@ -390,9 +539,27 @@ namespace MobileGL { case EGL_MAX_SWAP_INTERVAL: *value = cfg->MaxSwapInterval; return true; + case EGL_NATIVE_RENDERABLE: + *value = EGL_TRUE; + return true; case EGL_NATIVE_VISUAL_ID: *value = cfg->NativeVisualId; return true; + case EGL_NATIVE_VISUAL_TYPE: + *value = cfg->NativeVisualId; + return true; + case EGL_SAMPLE_BUFFERS: + case EGL_SAMPLES: + *value = 0; + return true; + case EGL_TRANSPARENT_TYPE: + *value = EGL_NONE; + return true; + case EGL_TRANSPARENT_RED_VALUE: + case EGL_TRANSPARENT_GREEN_VALUE: + case EGL_TRANSPARENT_BLUE_VALUE: + *value = 0; + return true; default: const_cast(this)->SetError(EGL_BAD_ATTRIBUTE); return false; diff --git a/MobileGL/MG_State/EGLState/Core.h b/MobileGL/MG_State/EGLState/Core.h index 45cb0674..04642794 100644 --- a/MobileGL/MG_State/EGLState/Core.h +++ b/MobileGL/MG_State/EGLState/Core.h @@ -213,7 +213,7 @@ namespace MobileGL { static std::thread::id CurrentThreadKey(); EGLDisplayHandle GetOrCreateDisplay(Uint64 nativeDisplayKey, EGLenum platform); - EGLConfigHandle CreateDefaultConfig(EGLDisplayHandle display); + EGLConfigHandle CreateDefaultConfig(EGLDisplayHandle display, EGLint configId, EGLint stencilSize); DisplayObject* TryGetDisplay(EGLDisplayHandle display); const DisplayObject* TryGetDisplay(EGLDisplayHandle display) const;