// MobileGL - MobileGL/MG_Impl/WGLImpl/WGLImpl.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 "WGLImpl.h" #if defined(_WIN32) #include "../EGLImpl/EGLImpl.h" #include "../GetProcAddress.h" #include namespace MobileGL::MG_Impl::WGLImpl { namespace { // WGL_ARB_pixel_format constexpr int WGL_NUMBER_PIXEL_FORMATS_ARB = 0x2000; constexpr int WGL_DRAW_TO_WINDOW_ARB = 0x2001; constexpr int WGL_DRAW_TO_BITMAP_ARB = 0x2002; constexpr int WGL_ACCELERATION_ARB = 0x2003; constexpr int WGL_NEED_PALETTE_ARB = 0x2004; constexpr int WGL_NEED_SYSTEM_PALETTE_ARB = 0x2005; constexpr int WGL_SWAP_LAYER_BUFFERS_ARB = 0x2006; constexpr int WGL_SWAP_METHOD_ARB = 0x2007; constexpr int WGL_NUMBER_OVERLAYS_ARB = 0x2008; constexpr int WGL_NUMBER_UNDERLAYS_ARB = 0x2009; constexpr int WGL_TRANSPARENT_ARB = 0x200A; constexpr int WGL_SHARE_DEPTH_ARB = 0x200C; constexpr int WGL_SHARE_STENCIL_ARB = 0x200D; constexpr int WGL_SHARE_ACCUM_ARB = 0x200E; constexpr int WGL_SUPPORT_GDI_ARB = 0x200F; constexpr int WGL_SUPPORT_OPENGL_ARB = 0x2010; constexpr int WGL_DOUBLE_BUFFER_ARB = 0x2011; constexpr int WGL_STEREO_ARB = 0x2012; constexpr int WGL_PIXEL_TYPE_ARB = 0x2013; constexpr int WGL_COLOR_BITS_ARB = 0x2014; constexpr int WGL_RED_BITS_ARB = 0x2015; constexpr int WGL_RED_SHIFT_ARB = 0x2016; constexpr int WGL_GREEN_BITS_ARB = 0x2017; constexpr int WGL_GREEN_SHIFT_ARB = 0x2018; constexpr int WGL_BLUE_BITS_ARB = 0x2019; constexpr int WGL_BLUE_SHIFT_ARB = 0x201A; constexpr int WGL_ALPHA_BITS_ARB = 0x201B; constexpr int WGL_ALPHA_SHIFT_ARB = 0x201C; constexpr int WGL_ACCUM_BITS_ARB = 0x201D; constexpr int WGL_ACCUM_RED_BITS_ARB = 0x201E; constexpr int WGL_ACCUM_GREEN_BITS_ARB = 0x201F; constexpr int WGL_ACCUM_BLUE_BITS_ARB = 0x2020; constexpr int WGL_ACCUM_ALPHA_BITS_ARB = 0x2021; constexpr int WGL_DEPTH_BITS_ARB = 0x2022; constexpr int WGL_STENCIL_BITS_ARB = 0x2023; constexpr int WGL_AUX_BUFFERS_ARB = 0x2024; constexpr int WGL_NO_ACCELERATION_ARB = 0x2025; constexpr int WGL_FULL_ACCELERATION_ARB = 0x2027; constexpr int WGL_SWAP_EXCHANGE_ARB = 0x2028; constexpr int WGL_TYPE_RGBA_ARB = 0x202B; // WGL_ARB_multisample constexpr int WGL_SAMPLE_BUFFERS_ARB = 0x2041; constexpr int WGL_SAMPLES_ARB = 0x2042; // WGL_ARB_create_context / _profile / _no_error constexpr int WGL_CONTEXT_MAJOR_VERSION_ARB = 0x2091; constexpr int WGL_CONTEXT_MINOR_VERSION_ARB = 0x2092; constexpr int WGL_CONTEXT_LAYER_PLANE_ARB = 0x2093; constexpr int WGL_CONTEXT_FLAGS_ARB = 0x2094; constexpr int WGL_CONTEXT_PROFILE_MASK_ARB = 0x9126; constexpr int WGL_CONTEXT_DEBUG_BIT_ARB = 0x0001; constexpr int WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB = 0x0002; constexpr int WGL_CONTEXT_CORE_PROFILE_BIT_ARB = 0x00000001; constexpr int WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB = 0x00000002; constexpr int WGL_CONTEXT_OPENGL_NO_ERROR_ARB = 0x31B3; constexpr DWORD ERROR_INVALID_VERSION_ARB = 0x2095; constexpr DWORD ERROR_INVALID_PROFILE_ARB = 0x2096; struct PixelFormatInfo { GLint AlphaBits; GLint DepthBits; GLint StencilBits; }; // Mirrors the two EGLState configs (RGBA8 + depth24, stencil 8 / stencil 0). constexpr PixelFormatInfo kPixelFormats[] = { {8, 24, 8}, {8, 24, 0}, }; constexpr int kPixelFormatCount = static_cast(std::size(kPixelFormats)); struct ContextObject { EGLDisplay Display = EGL_NO_DISPLAY; EGLConfig Config = nullptr; EGLContext Context = EGL_NO_CONTEXT; }; struct WindowSurface { EGLDisplay Display = EGL_NO_DISPLAY; EGLSurface Surface = EGL_NO_SURFACE; Uint32 Width = 0; Uint32 Height = 0; }; std::recursive_mutex& RegistryMutex() { static auto* mutex = new std::recursive_mutex(); return *mutex; } UnorderedMap& Contexts() { static auto* contexts = new UnorderedMap(); return *contexts; } UnorderedMap& WindowSurfaces() { static auto* surfaces = new UnorderedMap(); return *surfaces; } UnorderedMap& WindowPixelFormats() { static auto* formats = new UnorderedMap(); return *formats; } Uint64& NextContextHandle() { static auto* handle = new Uint64(0x10000); return *handle; } struct ThreadCurrent { HDC DC = nullptr; HGLRC Context = nullptr; }; thread_local ThreadCurrent t_current; Int& SwapIntervalShadow() { static auto* interval = new Int(1); return *interval; } void EnsureInitialized() { // Initialize() loads backend libraries and glslang, which must not run // under the loader lock; first WGL call is the earliest safe moment. // MobileGL::EnsureInitialized (not a local once_flag) so a fresh init // can follow a full teardown from the last eglTerminate. MobileGL::EnsureInitialized(); } EGLDisplay EnsureDisplay() { EnsureInitialized(); EGLDisplay display = EGLImpl::GetDisplay(EGL_DEFAULT_DISPLAY); if (display == EGL_NO_DISPLAY) { return EGL_NO_DISPLAY; } if (!EGLImpl::Initialize(display, nullptr, nullptr)) { return EGL_NO_DISPLAY; } return display; } HGLRC EncodeContext(Uint64 handle) { return reinterpret_cast(static_cast(handle)); } ContextObject* TryGetContext(HGLRC hglrc) { auto& contexts = Contexts(); auto it = contexts.find(hglrc); return it == contexts.end() ? nullptr : &it->second; } const PixelFormatInfo& PixelFormatForWindow(HWND hwnd) { auto& formats = WindowPixelFormats(); auto it = formats.find(hwnd); int index = it == formats.end() ? 1 : it->second; if (index < 1 || index > kPixelFormatCount) { index = 1; } return kPixelFormats[index - 1]; } Bool QueryClientSize(HWND hwnd, Uint32& width, Uint32& height) { RECT rect{}; if (!GetClientRect(hwnd, &rect)) { return false; } width = static_cast(std::max(rect.right - rect.left, 1)); height = static_cast(std::max(rect.bottom - rect.top, 1)); return true; } // The backends never query the HWND client size themselves; the WGL layer // owns size discovery and pushes changes through the internal resize hook // (same contract as the macOS CGL layer). void SyncSurfaceSize(HWND hwnd, WindowSurface& surface) { Uint32 width = 0; Uint32 height = 0; if (!QueryClientSize(hwnd, width, height)) { return; } if (width == surface.Width && height == surface.Height) { return; } if (EGLImpl::ResizePlatformWindowSurface(surface.Display, surface.Surface, static_cast(width), static_cast(height))) { surface.Width = width; surface.Height = height; } } WindowSurface* EnsureWindowSurface(HWND hwnd, const ContextObject& context) { auto& surfaces = WindowSurfaces(); auto it = surfaces.find(hwnd); if (it != surfaces.end()) { SyncSurfaceSize(hwnd, it->second); return &it->second; } Uint32 width = 0; Uint32 height = 0; if (!QueryClientSize(hwnd, width, height)) { MGLOG_E_ONCE("wgl: GetClientRect failed for HWND %p", hwnd); return nullptr; } const EGLAttrib attribs[] = { EGL_WIDTH, static_cast(width), EGL_HEIGHT, static_cast(height), EGL_NONE, }; EGLSurface surface = EGLImpl::CreatePlatformWindowSurface(context.Display, context.Config, hwnd, attribs); if (surface == EGL_NO_SURFACE) { MGLOG_E_ONCE("wgl: failed to create window surface for HWND %p (%ux%u)", hwnd, width, height); return nullptr; } WindowSurface record; record.Display = context.Display; record.Surface = surface; record.Width = width; record.Height = height; auto [inserted, _] = surfaces.emplace(hwnd, record); return &inserted->second; } HGLRC CreateContextFromEGLAttribs(HDC hdc, HGLRC share, const EGLint* contextAttribs) { const std::lock_guard lock(RegistryMutex()); EGLDisplay display = EnsureDisplay(); if (display == EGL_NO_DISPLAY) { MGLOG_E_ONCE("wgl: no EGL display"); return nullptr; } EGLImpl::BindAPI(EGL_OPENGL_API); EGLContext shareContext = EGL_NO_CONTEXT; if (share) { auto* shareObject = TryGetContext(share); if (!shareObject) { SetLastError(ERROR_INVALID_HANDLE); return nullptr; } shareContext = shareObject->Context; } HWND hwnd = WindowFromDC(hdc); const PixelFormatInfo& pixelFormat = PixelFormatForWindow(hwnd); const EGLint configAttribs[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, pixelFormat.AlphaBits, EGL_DEPTH_SIZE, pixelFormat.DepthBits, EGL_STENCIL_SIZE, pixelFormat.StencilBits, EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_NONE, }; EGLConfig config = nullptr; EGLint configCount = 0; if (!EGLImpl::ChooseConfig(display, configAttribs, &config, 1, &configCount) || configCount <= 0) { MGLOG_E_ONCE("wgl: eglChooseConfig failed"); return nullptr; } EGLContext eglContext = EGLImpl::CreateContext(display, config, shareContext, contextAttribs); if (eglContext == EGL_NO_CONTEXT) { MGLOG_E_ONCE("wgl: eglCreateContext failed"); return nullptr; } ContextObject object; object.Display = display; object.Config = config; object.Context = eglContext; const auto handle = EncodeContext(NextContextHandle()++); Contexts()[handle] = object; MGLOG_I("wgl: created context %p (EGL context %p)", handle, eglContext); return handle; } // ---- WGL extension entry points (resolved via wglGetProcAddress only) ---- const char* WINAPI Ext_GetExtensionsStringARB(HDC) { return "WGL_ARB_create_context WGL_ARB_create_context_no_error WGL_ARB_create_context_profile " "WGL_ARB_extensions_string WGL_ARB_pixel_format WGL_EXT_extensions_string WGL_EXT_swap_control"; } const char* WINAPI Ext_GetExtensionsStringEXT() { return Ext_GetExtensionsStringARB(nullptr); } HGLRC WINAPI Ext_CreateContextAttribsARB(HDC hdc, HGLRC hShareContext, const int* attribList) { EnsureInitialized(); int major = 1; int minor = 0; int profileMask = 0; int flags = 0; if (attribList) { for (SizeT i = 0; attribList[i] != 0; i += 2) { const int attrib = attribList[i]; const int value = attribList[i + 1]; switch (attrib) { case WGL_CONTEXT_MAJOR_VERSION_ARB: major = value; break; case WGL_CONTEXT_MINOR_VERSION_ARB: minor = value; break; case WGL_CONTEXT_PROFILE_MASK_ARB: profileMask = value; break; case WGL_CONTEXT_FLAGS_ARB: flags = value; break; case WGL_CONTEXT_LAYER_PLANE_ARB: if (value != 0) { SetLastError(ERROR_INVALID_PARAMETER); return nullptr; } break; case WGL_CONTEXT_OPENGL_NO_ERROR_ARB: // Accepted and ignored: MobileGL always validates. break; default: MGLOG_D("wglCreateContextAttribsARB: ignoring attrib 0x%04x = 0x%x", attrib, value); break; } } } if (major < 1 || (profileMask & ~(WGL_CONTEXT_CORE_PROFILE_BIT_ARB | WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB))) { SetLastError(profileMask ? ERROR_INVALID_PROFILE_ARB : ERROR_INVALID_VERSION_ARB); return nullptr; } Vector attribs = { EGL_CONTEXT_MAJOR_VERSION, major, EGL_CONTEXT_MINOR_VERSION, minor, }; const Bool wantsCompat = (profileMask & WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB) != 0; if (major > 3 || (major == 3 && minor >= 2) || profileMask != 0) { attribs.push_back(EGL_CONTEXT_OPENGL_PROFILE_MASK); attribs.push_back(wantsCompat ? EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT : EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT); } if (flags & WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB) { attribs.push_back(EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE); attribs.push_back(EGL_TRUE); } if (flags & WGL_CONTEXT_DEBUG_BIT_ARB) { attribs.push_back(EGL_CONTEXT_OPENGL_DEBUG); attribs.push_back(EGL_TRUE); } attribs.push_back(EGL_NONE); return CreateContextFromEGLAttribs(hdc, hShareContext, attribs.data()); } BOOL WINAPI Ext_SwapIntervalEXT(int interval) { const std::lock_guard lock(RegistryMutex()); EGLDisplay display = EnsureDisplay(); if (display == EGL_NO_DISPLAY) { return FALSE; } if (interval < 0) { // Adaptive vsync is not supported; clamp to regular vsync. interval = 1; } EGLImpl::SwapInterval(display, interval); SwapIntervalShadow() = interval; return TRUE; } int WINAPI Ext_GetSwapIntervalEXT() { const std::lock_guard lock(RegistryMutex()); return SwapIntervalShadow(); } int PixelFormatAttribValue(int format, int attrib) { const PixelFormatInfo& info = kPixelFormats[format - 1]; switch (attrib) { case WGL_NUMBER_PIXEL_FORMATS_ARB: return kPixelFormatCount; case WGL_SUPPORT_OPENGL_ARB: case WGL_DRAW_TO_WINDOW_ARB: case WGL_DOUBLE_BUFFER_ARB: return 1; case WGL_ACCELERATION_ARB: return WGL_FULL_ACCELERATION_ARB; case WGL_PIXEL_TYPE_ARB: return WGL_TYPE_RGBA_ARB; case WGL_COLOR_BITS_ARB: return 32; case WGL_RED_BITS_ARB: case WGL_GREEN_BITS_ARB: case WGL_BLUE_BITS_ARB: return 8; case WGL_RED_SHIFT_ARB: return 16; case WGL_GREEN_SHIFT_ARB: return 8; case WGL_BLUE_SHIFT_ARB: return 0; case WGL_ALPHA_BITS_ARB: return info.AlphaBits; case WGL_ALPHA_SHIFT_ARB: return 24; case WGL_DEPTH_BITS_ARB: return info.DepthBits; case WGL_STENCIL_BITS_ARB: return info.StencilBits; case WGL_SWAP_METHOD_ARB: return WGL_SWAP_EXCHANGE_ARB; case WGL_DRAW_TO_BITMAP_ARB: case WGL_NEED_PALETTE_ARB: case WGL_NEED_SYSTEM_PALETTE_ARB: case WGL_SWAP_LAYER_BUFFERS_ARB: case WGL_NUMBER_OVERLAYS_ARB: case WGL_NUMBER_UNDERLAYS_ARB: case WGL_TRANSPARENT_ARB: case WGL_SHARE_DEPTH_ARB: case WGL_SHARE_STENCIL_ARB: case WGL_SHARE_ACCUM_ARB: case WGL_SUPPORT_GDI_ARB: case WGL_STEREO_ARB: case WGL_ACCUM_BITS_ARB: case WGL_ACCUM_RED_BITS_ARB: case WGL_ACCUM_GREEN_BITS_ARB: case WGL_ACCUM_BLUE_BITS_ARB: case WGL_ACCUM_ALPHA_BITS_ARB: case WGL_AUX_BUFFERS_ARB: case WGL_SAMPLE_BUFFERS_ARB: case WGL_SAMPLES_ARB: default: return 0; } } BOOL WINAPI Ext_GetPixelFormatAttribivARB(HDC, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int* piAttributes, int* piValues) { if (iLayerPlane != 0 || !piAttributes || !piValues) { return FALSE; } // Format 0 is only valid for WGL_NUMBER_PIXEL_FORMATS_ARB queries. if (iPixelFormat < 0 || iPixelFormat > kPixelFormatCount) { return FALSE; } const int format = iPixelFormat == 0 ? 1 : iPixelFormat; for (UINT i = 0; i < nAttributes; ++i) { piValues[i] = PixelFormatAttribValue(format, piAttributes[i]); } return TRUE; } BOOL WINAPI Ext_GetPixelFormatAttribfvARB(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int* piAttributes, FLOAT* pfValues) { if (!pfValues) { return FALSE; } Vector values(nAttributes); if (!Ext_GetPixelFormatAttribivARB(hdc, iPixelFormat, iLayerPlane, nAttributes, piAttributes, values.data())) { return FALSE; } for (UINT i = 0; i < nAttributes; ++i) { pfValues[i] = static_cast(values[i]); } return TRUE; } BOOL WINAPI Ext_ChoosePixelFormatARB(HDC, const int* piAttribIList, const FLOAT*, UINT nMaxFormats, int* piFormats, UINT* nNumFormats) { if (!piFormats || !nNumFormats) { return FALSE; } int wantedStencil = 0; if (piAttribIList) { for (SizeT i = 0; piAttribIList[i] != 0; i += 2) { if (piAttribIList[i] == WGL_STENCIL_BITS_ARB) { wantedStencil = piAttribIList[i + 1]; } } } UINT count = 0; const int preferred = wantedStencil > 0 ? 1 : 2; const int fallback = wantedStencil > 0 ? 2 : 1; if (count < nMaxFormats) { piFormats[count++] = preferred; } if (count < nMaxFormats) { piFormats[count++] = fallback; } *nNumFormats = count; return TRUE; } struct WGLExtensionProc { const char* Name; PROC Proc; }; const WGLExtensionProc kWGLExtensionProcs[] = { {"wglGetExtensionsStringARB", reinterpret_cast(Ext_GetExtensionsStringARB)}, {"wglGetExtensionsStringEXT", reinterpret_cast(Ext_GetExtensionsStringEXT)}, {"wglCreateContextAttribsARB", reinterpret_cast(Ext_CreateContextAttribsARB)}, {"wglSwapIntervalEXT", reinterpret_cast(Ext_SwapIntervalEXT)}, {"wglGetSwapIntervalEXT", reinterpret_cast(Ext_GetSwapIntervalEXT)}, {"wglGetPixelFormatAttribivARB", reinterpret_cast(Ext_GetPixelFormatAttribivARB)}, {"wglGetPixelFormatAttribfvARB", reinterpret_cast(Ext_GetPixelFormatAttribfvARB)}, {"wglChoosePixelFormatARB", reinterpret_cast(Ext_ChoosePixelFormatARB)}, }; } // namespace int ChoosePixelFormat(HDC hdc, const PIXELFORMATDESCRIPTOR* pfd) { EnsureInitialized(); MGLOG_D("wglChoosePixelFormat(hdc=%p)", hdc); // Format 1 (RGBA8 + depth24/stencil8) satisfies every request; a format // exceeding the asked-for capabilities is a legal ChoosePixelFormat answer. (void)pfd; return 1; } int DescribePixelFormat(HDC hdc, int format, UINT size, PIXELFORMATDESCRIPTOR* pfd) { EnsureInitialized(); MGLOG_D("wglDescribePixelFormat(hdc=%p, format=%d)", hdc, format); if (!pfd) { return kPixelFormatCount; } if (size < sizeof(PIXELFORMATDESCRIPTOR) || format < 1 || format > kPixelFormatCount) { return 0; } const PixelFormatInfo& info = kPixelFormats[format - 1]; std::memset(pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); pfd->nSize = sizeof(PIXELFORMATDESCRIPTOR); pfd->nVersion = 1; pfd->dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_SWAP_EXCHANGE #if defined(PFD_SUPPORT_COMPOSITION) | PFD_SUPPORT_COMPOSITION #endif ; pfd->iPixelType = PFD_TYPE_RGBA; pfd->cColorBits = 32; pfd->cRedBits = 8; pfd->cRedShift = 16; pfd->cGreenBits = 8; pfd->cGreenShift = 8; pfd->cBlueBits = 8; pfd->cBlueShift = 0; pfd->cAlphaBits = static_cast(info.AlphaBits); pfd->cAlphaShift = 24; pfd->cDepthBits = static_cast(info.DepthBits); pfd->cStencilBits = static_cast(info.StencilBits); pfd->iLayerType = PFD_MAIN_PLANE; return kPixelFormatCount; } int GetPixelFormat(HDC hdc) { EnsureInitialized(); HWND hwnd = WindowFromDC(hdc); if (!hwnd) { return 0; } const std::lock_guard lock(RegistryMutex()); auto& formats = WindowPixelFormats(); auto it = formats.find(hwnd); return it == formats.end() ? 0 : it->second; } BOOL SetPixelFormat(HDC hdc, int format, const PIXELFORMATDESCRIPTOR*) { EnsureInitialized(); MGLOG_D("wglSetPixelFormat(hdc=%p, format=%d)", hdc, format); if (format < 1 || format > kPixelFormatCount) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } HWND hwnd = WindowFromDC(hdc); if (!hwnd) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } const std::lock_guard lock(RegistryMutex()); WindowPixelFormats()[hwnd] = format; return TRUE; } BOOL SwapBuffers(HDC hdc) { HWND hwnd = WindowFromDC(hdc); if (!hwnd) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } const std::lock_guard lock(RegistryMutex()); auto& surfaces = WindowSurfaces(); auto it = surfaces.find(hwnd); if (it == surfaces.end()) { MGLOG_W_ONCE("wglSwapBuffers: no surface for HWND %p", hwnd); return FALSE; } SyncSurfaceSize(hwnd, it->second); return EGLImpl::SwapBuffers(it->second.Display, it->second.Surface) == EGL_TRUE ? TRUE : FALSE; } HGLRC CreateContext(HDC hdc) { EnsureInitialized(); MGLOG_I("wglCreateContext(hdc=%p)", hdc); // A legacy WGL context is a compatibility-profile context; MobileGL keys // its relaxed-semantics mode off the explicit compatibility bit. const EGLint attribs[] = { EGL_CONTEXT_MAJOR_VERSION, 3, EGL_CONTEXT_MINOR_VERSION, 3, EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT, EGL_NONE, }; return CreateContextFromEGLAttribs(hdc, nullptr, attribs); } BOOL DeleteContext(HGLRC hglrc) { EnsureInitialized(); MGLOG_I("wglDeleteContext(%p)", hglrc); if (t_current.Context == hglrc) { MakeCurrent(nullptr, nullptr); } const std::lock_guard lock(RegistryMutex()); auto* object = TryGetContext(hglrc); if (!object) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } if (object->Context != EGL_NO_CONTEXT) { EGLImpl::DestroyContext(object->Display, object->Context); } Contexts().erase(hglrc); return TRUE; } BOOL MakeCurrent(HDC hdc, HGLRC hglrc) { EnsureInitialized(); MGLOG_D("wglMakeCurrent(hdc=%p, hglrc=%p)", hdc, hglrc); if (!hglrc) { if (!t_current.Context) { t_current = {}; return TRUE; } const EGLBoolean released = EGLImpl::MakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); t_current = {}; return released == EGL_TRUE ? TRUE : FALSE; } HWND hwnd = WindowFromDC(hdc); if (!hwnd) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } const std::lock_guard lock(RegistryMutex()); auto* object = TryGetContext(hglrc); if (!object) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } WindowSurface* surface = EnsureWindowSurface(hwnd, *object); if (!surface) { return FALSE; } if (!EGLImpl::MakeCurrent(object->Display, surface->Surface, surface->Surface, object->Context)) { MGLOG_E_ONCE("wglMakeCurrent: eglMakeCurrent failed (hdc=%p, hglrc=%p)", hdc, hglrc); return FALSE; } t_current = {hdc, hglrc}; return TRUE; } HGLRC GetCurrentContext() { return t_current.Context; } HDC GetCurrentDC() { return t_current.DC; } BOOL ShareLists(HGLRC hglrcShare, HGLRC hglrcDest) { // All MobileGL contexts alias one global GL object namespace, so every // pair of contexts already shares. const std::lock_guard lock(RegistryMutex()); if (!TryGetContext(hglrcShare) || !TryGetContext(hglrcDest)) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } return TRUE; } PROC GetProcAddress(const char* name) { EnsureInitialized(); if (!name) { return nullptr; } if (name[0] == 'w' && name[1] == 'g' && name[2] == 'l') { for (const auto& entry : kWGLExtensionProcs) { if (std::strcmp(entry.Name, name) == 0) { return entry.Proc; } } MGLOG_D("wglGetProcAddress: unknown wgl entry point %s", name); return nullptr; } return reinterpret_cast(MG_Impl::GetProcAddress(name)); } } // namespace MobileGL::MG_Impl::WGLImpl #endif // _WIN32