Files
MobileGL/MobileGL/MG_Impl/GLXImpl/GLXImpl.h
T
BZLZHH 08f98ad9ce [Feat] (MG_Impl): implement GLX 1.4 on the EGL layer so GLFW apps run on Linux
Desktop Linux GL apps (GLFW/LWJGL, glxgears, anything X11) create contexts
through GLX, and MobileGL only spoke EGL - the two exported glX symbols were
proc-address stubs that could resolve GL entry points but never produce a
context. GLXImpl is the missing sibling of WGLImpl/CGLImpl: the same
window-system-binding pattern, calling the internal MG_Impl::EGLImpl namespace
directly.

The surface covers exactly what GLFW 3.4 resolves via dlsym plus the legacy
visual API: FBConfig enumeration mirrors the two EGLState configs (stencil-8
first so stencil-wanting choosers land on it), glXGetVisualFromFBConfig answers
with the screen's default visual (falling back to any 24-bit TrueColor one),
and glXCreateContextAttribsARB maps the ARB attribs onto EGL context attribs
the way WGL's Ext_CreateContextAttribsARB does - profile mask only emitted for
3.2+ or an explicit profile request, since that bit is what keys MobileGL's
relaxed-semantics compatibility mode. Legacy glXCreateContext/CreateNewContext
hand out 3.3 compatibility contexts, matching wglCreateContext.

Drawables follow the WGL HWND model: the GLXWindow is the X window itself, the
EGL window surface is created lazily on first MakeCurrent and cached per XID,
and the GLX layer owns size discovery per the platform-layer contract - it
pushes changes through EGLImpl::ResizePlatformWindowSurface, polling
XGetGeometry on MakeCurrent and on swaps throttled to 250ms so a fast-swapping
app is not paying a server round trip per frame. libX11 is dlopen'd at runtime
like everywhere else in the tree; Xlib.h is already in every TU via the vulkan
include, so XVisualInfo gets an ABI mirror struct (Xutil.h needs the Bool and
Status macros that Includes.h deliberately pops) and the caller's XFree pairs
with our malloc.

glXGetProcAddress now resolves glX names from the export table before falling
through to the shared GL resolver, which previously returned nullptr for every
glX extension entry point - GLFW requires glXCreateContextAttribsARB and
glXSwapIntervalEXT to arrive that way.

Verified with a smoke test replaying GLFW's exact call sequence (dlsym-only
resolution, manual FBConfig filtering, 3.2 core forward-compatible context,
glXCreateWindow, 60 swapped frames, clean glGetError) on both backends against
the real NVIDIA driver, then with Minecraft 1.21.1, 1.21.4+Fabric+Sodium and
26.2-snapshot-6 reaching in-world rendering on both Espryt and Magma.
2026-08-05 23:08:29 -04:00

70 lines
3.4 KiB
C++

// MobileGL - MobileGL/MG_Impl/GLXImpl/GLXImpl.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 <Includes.h>
#if defined(__linux__) && !defined(__ANDROID__)
namespace MobileGL::MG_Impl::GLXImpl {
// GLX layered on MobileGL's own EGL, mirroring WGLImpl/CGLImpl. Handles are
// opaque to callers; XVisualInfo crosses the ABI as void* so this header
// needs no Xlib includes (Includes.h forward-declares Display/XID/Window).
using GLXFBConfigHandle = void*;
using GLXContextHandle = void*;
using GLXDrawableHandle = unsigned long; // XID
int QueryExtension(Display* dpy, int* errorBase, int* eventBase);
int QueryVersion(Display* dpy, int* major, int* minor);
const char* QueryExtensionsString(Display* dpy, int screen);
const char* GetClientString(Display* dpy, int name);
const char* QueryServerString(Display* dpy, int screen, int name);
GLXFBConfigHandle* GetFBConfigs(Display* dpy, int screen, int* nelements);
GLXFBConfigHandle* ChooseFBConfig(Display* dpy, int screen, const int* attribList, int* nelements);
int GetFBConfigAttrib(Display* dpy, GLXFBConfigHandle config, int attribute, int* value);
void* GetVisualFromFBConfig(Display* dpy, GLXFBConfigHandle config);
void* ChooseVisual(Display* dpy, int screen, int* attribList);
int GetConfig(Display* dpy, void* visualInfo, int attribute, int* value);
GLXContextHandle CreateContext(Display* dpy, void* visualInfo, GLXContextHandle share, int direct);
GLXContextHandle CreateNewContext(Display* dpy, GLXFBConfigHandle config, int renderType,
GLXContextHandle share, int direct);
GLXContextHandle CreateContextAttribsARB(Display* dpy, GLXFBConfigHandle config, GLXContextHandle share,
int direct, const int* attribList);
void DestroyContext(Display* dpy, GLXContextHandle context);
int MakeCurrent(Display* dpy, GLXDrawableHandle drawable, GLXContextHandle context);
int MakeContextCurrent(Display* dpy, GLXDrawableHandle draw, GLXDrawableHandle read,
GLXContextHandle context);
void SwapBuffers(Display* dpy, GLXDrawableHandle drawable);
GLXDrawableHandle CreateWindow(Display* dpy, GLXFBConfigHandle config, GLXDrawableHandle window,
const int* attribList);
void DestroyWindow(Display* dpy, GLXDrawableHandle window);
GLXContextHandle GetCurrentContext();
GLXDrawableHandle GetCurrentDrawable();
GLXDrawableHandle GetCurrentReadDrawable();
Display* GetCurrentDisplay();
int IsDirect(Display* dpy, GLXContextHandle context);
void WaitGL();
void WaitX();
int QueryContext(Display* dpy, GLXContextHandle context, int attribute, int* value);
void QueryDrawable(Display* dpy, GLXDrawableHandle drawable, int attribute, unsigned int* value);
void SwapIntervalEXT(Display* dpy, GLXDrawableHandle drawable, int interval);
int SwapIntervalMESA(unsigned int interval);
int GetSwapIntervalMESA();
int SwapIntervalSGI(int interval);
// Name -> exported glX entry point (table lives with the exports).
void* GetGLXEntryPoint(const char* name);
} // namespace MobileGL::MG_Impl::GLXImpl
#endif // __linux__ && !__ANDROID__