mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Feat] (WGL): add Windows host layer - drop-in opengl32.dll with WGL over EGLImpl, Win32 window backend plumbing for both backends, ANGLE loader path, and leak-at-exit process teardown
This commit is contained in:
@@ -303,6 +303,13 @@ if (ANDROID)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
list(APPEND SOURCE_FILES
|
||||||
|
MobileGL/MG_Impl/WGLImpl/WGLImpl.cpp
|
||||||
|
MobileGL/MG_Impl/WGLImpl/Exporting/Definitions.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(MOBILEGL_LINK_LIBRARIES
|
set(MOBILEGL_LINK_LIBRARIES
|
||||||
glslang::glslang
|
glslang::glslang
|
||||||
spirv-cross-c
|
spirv-cross-c
|
||||||
@@ -335,6 +342,14 @@ add_library(${CMAKE_PROJECT_NAME} SHARED
|
|||||||
${SOURCE_FILES}
|
${SOURCE_FILES}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
# The wgl* entry points are exported via .def (see the comment in wgl.def);
|
||||||
|
# only the shared library links it.
|
||||||
|
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
|
||||||
|
MobileGL/MG_Impl/WGLImpl/Exporting/wgl.def
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES
|
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES
|
||||||
C_VISIBILITY_PRESET default
|
C_VISIBILITY_PRESET default
|
||||||
@@ -377,6 +392,18 @@ if(UNIX AND NOT APPLE AND NOT ANDROID)
|
|||||||
endforeach()
|
endforeach()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
# Drop-in for the classic GL loader path: a copy named opengl32.dll placed
|
||||||
|
# next to a host executable is what LoadLibrary("opengl32.dll") and gdi32's
|
||||||
|
# pixel-format forwarding will resolve.
|
||||||
|
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
"$<TARGET_FILE:${CMAKE_PROJECT_NAME}>"
|
||||||
|
"$<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>/opengl32.dll"
|
||||||
|
COMMENT "Creating opengl32.dll drop-in copy"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NOT ANDROID)
|
if(NOT ANDROID)
|
||||||
add_library(${CMAKE_PROJECT_NAME}_s STATIC
|
add_library(${CMAKE_PROJECT_NAME}_s STATIC
|
||||||
${SOURCE_FILES}
|
${SOURCE_FILES}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
#define MOBILEGL_EGL_API MOBILEGL_API
|
#define MOBILEGL_EGL_API MOBILEGL_API
|
||||||
#define MOBILEGL_CGL_API MOBILEGL_API
|
#define MOBILEGL_CGL_API MOBILEGL_API
|
||||||
#define MOBILEGL_NSOPENGL_API MOBILEGL_API
|
#define MOBILEGL_NSOPENGL_API MOBILEGL_API
|
||||||
|
#define MOBILEGL_WGL_API MOBILEGL_API
|
||||||
|
|
||||||
// ====================== MobileGL configurations ======================= //
|
// ====================== MobileGL configurations ======================= //
|
||||||
#ifndef MOBILEGL_LOG_ACTIVE_LEVEL
|
#ifndef MOBILEGL_LOG_ACTIVE_LEVEL
|
||||||
|
|||||||
+30
-11
@@ -9,6 +9,7 @@
|
|||||||
#include "Init.h"
|
#include "Init.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include <MG_Backend/BackendObjects.h>
|
#include <MG_Backend/BackendObjects.h>
|
||||||
|
#include <MG_Backend/DirectVulkan/DirectVulkan.h>
|
||||||
#include <MG_State/GLState/Core.h>
|
#include <MG_State/GLState/Core.h>
|
||||||
#include <MG_State/EGLState/Core.h>
|
#include <MG_State/EGLState/Core.h>
|
||||||
#include <MG_Impl/GLImpl/Texture/ProxyTexture.h>
|
#include <MG_Impl/GLImpl/Texture/ProxyTexture.h>
|
||||||
@@ -87,17 +88,35 @@ namespace MobileGL {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
namespace {
|
||||||
switch (ul_reason_for_call) {
|
// Called on DLL_PROCESS_DETACH, which runs BEFORE the CRT executes this
|
||||||
case DLL_PROCESS_ATTACH:
|
// DLL's static destructors. Releasing (deliberately leaking) the global
|
||||||
Initialize();
|
// singletons here keeps those destructors from tearing down the backend
|
||||||
break;
|
// inside a dying process - other threads have already been terminated,
|
||||||
|
// so Vulkan/GLES cleanup crashes with the process half-dead. The process
|
||||||
case DLL_PROCESS_DETACH:
|
// is exiting; the OS reclaims everything.
|
||||||
Destroy();
|
void AbandonAtProcessExit() {
|
||||||
break;
|
MGLOG_I("MobileGL: process detach, abandoning global state");
|
||||||
|
MG_Backend::DirectVulkan::pVulkanRenderer.release();
|
||||||
|
MG_Backend::pActiveBackendObject.release();
|
||||||
|
MG_State::pGLContext.release();
|
||||||
|
MG_State::pEGLContext.release();
|
||||||
|
MG_Impl::GLImpl::TextureImpl::pProxyTextureManager.release();
|
||||||
|
MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo.release();
|
||||||
|
MGLOG_I("MobileGL: global state abandoned");
|
||||||
}
|
}
|
||||||
return TRUE;
|
} // namespace
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
} // namespace MobileGL
|
} // namespace MobileGL
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// Initialization is NOT done here: MobileGL::Initialize() loads libraries
|
||||||
|
// (Vulkan/ANGLE) and spins up glslang, none of which is safe under the loader
|
||||||
|
// lock. The WGL host layer calls Initialize() lazily on its first entry point.
|
||||||
|
extern "C" BOOL WINAPI DllMain(HINSTANCE, DWORD reason, LPVOID) {
|
||||||
|
if (reason == DLL_PROCESS_DETACH) {
|
||||||
|
MobileGL::AbandonAtProcessExit();
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -313,7 +313,8 @@ namespace MobileGL {
|
|||||||
Android,
|
Android,
|
||||||
X11,
|
X11,
|
||||||
MetalLayer,
|
MetalLayer,
|
||||||
// TODO: Wayland, Windows, etc.
|
Win32, // Handle is an HWND
|
||||||
|
// TODO: Wayland, etc.
|
||||||
WindowBackendCount,
|
WindowBackendCount,
|
||||||
Unknown = -1
|
Unknown = -1
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -701,9 +701,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
|
|
||||||
if ((handle.Backend != WindowBackend::Android &&
|
if ((handle.Backend != WindowBackend::Android &&
|
||||||
handle.Backend != WindowBackend::X11 &&
|
handle.Backend != WindowBackend::X11 &&
|
||||||
handle.Backend != WindowBackend::MetalLayer) ||
|
handle.Backend != WindowBackend::MetalLayer &&
|
||||||
|
handle.Backend != WindowBackend::Win32) ||
|
||||||
!handle.Handle) {
|
!handle.Handle) {
|
||||||
MGLOG_E("DirectGLES backend only supports Android, X11, and CAMetalLayer native windows");
|
MGLOG_E("DirectGLES backend only supports Android, X11, CAMetalLayer, and Win32 native windows");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4307,9 +4307,23 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
|
|
||||||
if (!ChooseConfigForSurface(surfaceBit, g_Config, window)) return false;
|
if (!ChooseConfigForSurface(surfaceBit, g_Config, window)) return false;
|
||||||
|
|
||||||
const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
|
// Negotiate the highest ES 3.x context. Version-strict EGL implementations
|
||||||
|
// (ANGLE) return exactly the requested minor, and a bare CLIENT_VERSION 3
|
||||||
|
// request yields a 3.0 context that lacks the 3.1/3.2 texture targets the
|
||||||
|
// capability probes exercise; mobile drivers ignore the minor and hand out
|
||||||
|
// their maximum either way.
|
||||||
|
for (const EGLint minorVersion : {2, 1, 0}) {
|
||||||
|
const EGLint contextAttribs[] = {EGL_CONTEXT_MAJOR_VERSION, 3,
|
||||||
|
EGL_CONTEXT_MINOR_VERSION, minorVersion,
|
||||||
|
EGL_NONE};
|
||||||
|
g_Context = g_EGLFuncs.eglCreateContext(g_Display, g_Config, EGL_NO_CONTEXT, contextAttribs);
|
||||||
|
if (g_Context != EGL_NO_CONTEXT) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
g_Context = g_EGLFuncs.eglCreateContext(g_Display, g_Config, EGL_NO_CONTEXT, contextAttribs);
|
const EGLint legacyContextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
|
||||||
|
g_Context = g_EGLFuncs.eglCreateContext(g_Display, g_Config, EGL_NO_CONTEXT, legacyContextAttribs);
|
||||||
return g_Context != EGL_NO_CONTEXT;
|
return g_Context != EGL_NO_CONTEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -397,8 +397,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
if (!handle.Handle || (handle.Backend != WindowBackend::Android &&
|
if (!handle.Handle || (handle.Backend != WindowBackend::Android &&
|
||||||
handle.Backend != WindowBackend::X11 &&
|
handle.Backend != WindowBackend::X11 &&
|
||||||
handle.Backend != WindowBackend::MetalLayer)) {
|
handle.Backend != WindowBackend::MetalLayer &&
|
||||||
MGLOG_E("DirectVulkan backend only supports Android, X11, and CAMetalLayer native windows");
|
handle.Backend != WindowBackend::Win32)) {
|
||||||
|
MGLOG_E("DirectVulkan backend only supports Android, X11, CAMetalLayer, and Win32 native windows");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7560,6 +7560,10 @@ void main() {
|
|||||||
MOBILEGL_ASSERT(hwnd, "HWND is null");
|
MOBILEGL_ASSERT(hwnd, "HWND is null");
|
||||||
|
|
||||||
VkWin32SurfaceCreateInfoKHR sci{VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR};
|
VkWin32SurfaceCreateInfoKHR sci{VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR};
|
||||||
|
sci.hinstance = reinterpret_cast<HINSTANCE>(GetWindowLongPtrW(hwnd, GWLP_HINSTANCE));
|
||||||
|
if (!sci.hinstance) {
|
||||||
|
sci.hinstance = GetModuleHandleW(nullptr);
|
||||||
|
}
|
||||||
sci.hwnd = hwnd;
|
sci.hwnd = hwnd;
|
||||||
VK_VERIFY(vkCreateWin32SurfaceKHR(m_instance, &sci, nullptr, &m_surface), "vkCreateWin32SurfaceKHR failed");
|
VK_VERIFY(vkCreateWin32SurfaceKHR(m_instance, &sci, nullptr, &m_surface), "vkCreateWin32SurfaceKHR failed");
|
||||||
#elif defined VK_USE_PLATFORM_METAL_EXT
|
#elif defined VK_USE_PLATFORM_METAL_EXT
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
|||||||
return MG_Backend::WindowBackend::Android;
|
return MG_Backend::WindowBackend::Android;
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
return MG_Backend::WindowBackend::MetalLayer;
|
return MG_Backend::WindowBackend::MetalLayer;
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
return MG_Backend::WindowBackend::Win32;
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
return MG_Backend::WindowBackend::X11;
|
return MG_Backend::WindowBackend::X11;
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Impl/WGLImpl/Exporting/Definitions.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
|
||||||
|
|
||||||
|
// wingdi.h declares most wgl* entry points as WINGDIAPI (__declspec(dllimport)),
|
||||||
|
// which would reject our definitions. _GDI32_ is the SDK's "I am the module that
|
||||||
|
// implements these" switch: it turns WINGDIAPI into a plain declaration. It must
|
||||||
|
// be defined before the first windows.h inclusion in this translation unit.
|
||||||
|
#if defined(_WIN32) && !defined(_GDI32_)
|
||||||
|
#define _GDI32_ 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <Includes.h>
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#include "../WGLImpl.h"
|
||||||
|
|
||||||
|
namespace WGL = MobileGL::MG_Impl::WGLImpl;
|
||||||
|
|
||||||
|
// ---- Pixel-format entry points (gdi32 forwards ChoosePixelFormat/SetPixelFormat/
|
||||||
|
// ---- DescribePixelFormat/GetPixelFormat/SwapBuffers into these exports) ----
|
||||||
|
|
||||||
|
extern "C" int WINAPI wglChoosePixelFormat(HDC hdc, CONST PIXELFORMATDESCRIPTOR* ppfd) {
|
||||||
|
return WGL::ChoosePixelFormat(hdc, ppfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" int WINAPI wglDescribePixelFormat(HDC hdc, int iPixelFormat, UINT nBytes,
|
||||||
|
LPPIXELFORMATDESCRIPTOR ppfd) {
|
||||||
|
return WGL::DescribePixelFormat(hdc, iPixelFormat, nBytes, ppfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" int WINAPI wglGetPixelFormat(HDC hdc) {
|
||||||
|
return WGL::GetPixelFormat(hdc);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" BOOL WINAPI wglSetPixelFormat(HDC hdc, int iPixelFormat, CONST PIXELFORMATDESCRIPTOR* ppfd) {
|
||||||
|
return WGL::SetPixelFormat(hdc, iPixelFormat, ppfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" BOOL WINAPI wglSwapBuffers(HDC hdc) {
|
||||||
|
return WGL::SwapBuffers(hdc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Context management ----
|
||||||
|
|
||||||
|
extern "C" HGLRC WINAPI wglCreateContext(HDC hdc) {
|
||||||
|
return WGL::CreateContext(hdc);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" HGLRC WINAPI wglCreateLayerContext(HDC hdc, int iLayerPlane) {
|
||||||
|
return iLayerPlane == 0 ? WGL::CreateContext(hdc) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" BOOL WINAPI wglCopyContext(HGLRC, HGLRC, UINT) {
|
||||||
|
MGLOG_W("wglCopyContext is not supported");
|
||||||
|
SetLastError(ERROR_NOT_SUPPORTED);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" BOOL WINAPI wglDeleteContext(HGLRC hglrc) {
|
||||||
|
return WGL::DeleteContext(hglrc);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" HGLRC WINAPI wglGetCurrentContext(VOID) {
|
||||||
|
return WGL::GetCurrentContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" HDC WINAPI wglGetCurrentDC(VOID) {
|
||||||
|
return WGL::GetCurrentDC();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" BOOL WINAPI wglMakeCurrent(HDC hdc, HGLRC hglrc) {
|
||||||
|
return WGL::MakeCurrent(hdc, hglrc);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" BOOL WINAPI wglShareLists(HGLRC hglrcShare, HGLRC hglrcDest) {
|
||||||
|
return WGL::ShareLists(hglrcShare, hglrcDest);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Proc address ----
|
||||||
|
|
||||||
|
extern "C" PROC WINAPI wglGetProcAddress(LPCSTR lpszProc) {
|
||||||
|
return WGL::GetProcAddress(lpszProc);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" PROC WINAPI wglGetDefaultProcAddress(LPCSTR lpszProc) {
|
||||||
|
return WGL::GetProcAddress(lpszProc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Layer planes and palettes (unsupported; overlay planes do not exist here) ----
|
||||||
|
|
||||||
|
extern "C" BOOL WINAPI wglDescribeLayerPlane(HDC, int, int, UINT, LPLAYERPLANEDESCRIPTOR) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" int WINAPI wglSetLayerPaletteEntries(HDC, int, int, int, CONST COLORREF*) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" int WINAPI wglGetLayerPaletteEntries(HDC, int, int, int, COLORREF*) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" BOOL WINAPI wglRealizeLayerPalette(HDC, int, BOOL) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" BOOL WINAPI wglSwapLayerBuffers(HDC hdc, UINT fuPlanes) {
|
||||||
|
if (fuPlanes & WGL_SWAP_MAIN_PLANE) {
|
||||||
|
return WGL::SwapBuffers(hdc);
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" DWORD WINAPI wglSwapMultipleBuffers(UINT n, CONST WGLSWAP* ps) {
|
||||||
|
if (!ps) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
DWORD swapped = 0;
|
||||||
|
for (UINT i = 0; i < n; ++i) {
|
||||||
|
if (WGL::SwapBuffers(ps[i].hdc)) {
|
||||||
|
++swapped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return swapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Font rendering (legacy immediate-mode feature; not supported) ----
|
||||||
|
|
||||||
|
extern "C" BOOL WINAPI wglUseFontBitmapsA(HDC, DWORD, DWORD, DWORD) {
|
||||||
|
MGLOG_W("wglUseFontBitmapsA is not supported");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" BOOL WINAPI wglUseFontBitmapsW(HDC, DWORD, DWORD, DWORD) {
|
||||||
|
MGLOG_W("wglUseFontBitmapsW is not supported");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" BOOL WINAPI wglUseFontOutlinesA(HDC, DWORD, DWORD, DWORD, FLOAT, FLOAT, int,
|
||||||
|
LPGLYPHMETRICSFLOAT) {
|
||||||
|
MGLOG_W("wglUseFontOutlinesA is not supported");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" BOOL WINAPI wglUseFontOutlinesW(HDC, DWORD, DWORD, DWORD, FLOAT, FLOAT, int,
|
||||||
|
LPGLYPHMETRICSFLOAT) {
|
||||||
|
MGLOG_W("wglUseFontOutlinesW is not supported");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _WIN32
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
; MobileGL WGL exports. The wgl* entry points are defined without
|
||||||
|
; __declspec(dllexport) because wingdi.h pre-declares them (with _GDI32_ they
|
||||||
|
; become plain declarations, and MSVC rejects adding dllexport afterwards),
|
||||||
|
; so this .def file is what actually exports them from the DLL.
|
||||||
|
EXPORTS
|
||||||
|
wglChoosePixelFormat
|
||||||
|
wglCopyContext
|
||||||
|
wglCreateContext
|
||||||
|
wglCreateLayerContext
|
||||||
|
wglDeleteContext
|
||||||
|
wglDescribeLayerPlane
|
||||||
|
wglDescribePixelFormat
|
||||||
|
wglGetCurrentContext
|
||||||
|
wglGetCurrentDC
|
||||||
|
wglGetDefaultProcAddress
|
||||||
|
wglGetLayerPaletteEntries
|
||||||
|
wglGetPixelFormat
|
||||||
|
wglGetProcAddress
|
||||||
|
wglMakeCurrent
|
||||||
|
wglRealizeLayerPalette
|
||||||
|
wglSetLayerPaletteEntries
|
||||||
|
wglSetPixelFormat
|
||||||
|
wglShareLists
|
||||||
|
wglSwapBuffers
|
||||||
|
wglSwapLayerBuffers
|
||||||
|
wglSwapMultipleBuffers
|
||||||
|
wglUseFontBitmapsA
|
||||||
|
wglUseFontBitmapsW
|
||||||
|
wglUseFontOutlinesA
|
||||||
|
wglUseFontOutlinesW
|
||||||
@@ -0,0 +1,731 @@
|
|||||||
|
// 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 <Init.h>
|
||||||
|
|
||||||
|
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<int>(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<HGLRC, ContextObject>& Contexts() {
|
||||||
|
static auto* contexts = new UnorderedMap<HGLRC, ContextObject>();
|
||||||
|
return *contexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnorderedMap<HWND, WindowSurface>& WindowSurfaces() {
|
||||||
|
static auto* surfaces = new UnorderedMap<HWND, WindowSurface>();
|
||||||
|
return *surfaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnorderedMap<HWND, int>& WindowPixelFormats() {
|
||||||
|
static auto* formats = new UnorderedMap<HWND, int>();
|
||||||
|
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.
|
||||||
|
static std::once_flag once;
|
||||||
|
std::call_once(once, [] { MobileGL::Initialize(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
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<HGLRC>(static_cast<SizeT>(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<Uint32>(std::max<LONG>(rect.right - rect.left, 1));
|
||||||
|
height = static_cast<Uint32>(std::max<LONG>(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<EGLint>(width), static_cast<EGLint>(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("wgl: GetClientRect failed for HWND %p", hwnd);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EGLAttrib attribs[] = {
|
||||||
|
EGL_WIDTH, static_cast<EGLAttrib>(width),
|
||||||
|
EGL_HEIGHT, static_cast<EGLAttrib>(height),
|
||||||
|
EGL_NONE,
|
||||||
|
};
|
||||||
|
EGLSurface surface =
|
||||||
|
EGLImpl::CreatePlatformWindowSurface(context.Display, context.Config, hwnd, attribs);
|
||||||
|
if (surface == EGL_NO_SURFACE) {
|
||||||
|
MGLOG_E("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<std::recursive_mutex> lock(RegistryMutex());
|
||||||
|
EGLDisplay display = EnsureDisplay();
|
||||||
|
if (display == EGL_NO_DISPLAY) {
|
||||||
|
MGLOG_E("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("wgl: eglChooseConfig failed");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLContext eglContext = EGLImpl::CreateContext(display, config, shareContext, contextAttribs);
|
||||||
|
if (eglContext == EGL_NO_CONTEXT) {
|
||||||
|
MGLOG_E("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<EGLint> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<int> 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<FLOAT>(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<PROC>(Ext_GetExtensionsStringARB)},
|
||||||
|
{"wglGetExtensionsStringEXT", reinterpret_cast<PROC>(Ext_GetExtensionsStringEXT)},
|
||||||
|
{"wglCreateContextAttribsARB", reinterpret_cast<PROC>(Ext_CreateContextAttribsARB)},
|
||||||
|
{"wglSwapIntervalEXT", reinterpret_cast<PROC>(Ext_SwapIntervalEXT)},
|
||||||
|
{"wglGetSwapIntervalEXT", reinterpret_cast<PROC>(Ext_GetSwapIntervalEXT)},
|
||||||
|
{"wglGetPixelFormatAttribivARB", reinterpret_cast<PROC>(Ext_GetPixelFormatAttribivARB)},
|
||||||
|
{"wglGetPixelFormatAttribfvARB", reinterpret_cast<PROC>(Ext_GetPixelFormatAttribfvARB)},
|
||||||
|
{"wglChoosePixelFormatARB", reinterpret_cast<PROC>(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<BYTE>(info.AlphaBits);
|
||||||
|
pfd->cAlphaShift = 24;
|
||||||
|
pfd->cDepthBits = static_cast<BYTE>(info.DepthBits);
|
||||||
|
pfd->cStencilBits = static_cast<BYTE>(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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> lock(RegistryMutex());
|
||||||
|
auto& surfaces = WindowSurfaces();
|
||||||
|
auto it = surfaces.find(hwnd);
|
||||||
|
if (it == surfaces.end()) {
|
||||||
|
MGLOG_W("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<std::recursive_mutex> 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<std::recursive_mutex> 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("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<std::recursive_mutex> 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<PROC>(MG_Impl::GetProcAddress(name));
|
||||||
|
}
|
||||||
|
} // namespace MobileGL::MG_Impl::WGLImpl
|
||||||
|
|
||||||
|
#endif // _WIN32
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Impl/WGLImpl/WGLImpl.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(_WIN32)
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Impl::WGLImpl {
|
||||||
|
// Classic opengl32.dll surface. gdi32's ChoosePixelFormat/SetPixelFormat/
|
||||||
|
// DescribePixelFormat/GetPixelFormat/SwapBuffers forward into the loaded
|
||||||
|
// opengl32.dll's wgl* exports, so these back both call paths.
|
||||||
|
int ChoosePixelFormat(HDC hdc, const PIXELFORMATDESCRIPTOR* pfd);
|
||||||
|
int DescribePixelFormat(HDC hdc, int format, UINT size, PIXELFORMATDESCRIPTOR* pfd);
|
||||||
|
int GetPixelFormat(HDC hdc);
|
||||||
|
BOOL SetPixelFormat(HDC hdc, int format, const PIXELFORMATDESCRIPTOR* pfd);
|
||||||
|
BOOL SwapBuffers(HDC hdc);
|
||||||
|
|
||||||
|
HGLRC CreateContext(HDC hdc);
|
||||||
|
BOOL DeleteContext(HGLRC hglrc);
|
||||||
|
BOOL MakeCurrent(HDC hdc, HGLRC hglrc);
|
||||||
|
HGLRC GetCurrentContext();
|
||||||
|
HDC GetCurrentDC();
|
||||||
|
BOOL ShareLists(HGLRC hglrcShare, HGLRC hglrcDest);
|
||||||
|
|
||||||
|
PROC GetProcAddress(const char* name);
|
||||||
|
} // namespace MobileGL::MG_Impl::WGLImpl
|
||||||
|
|
||||||
|
#endif // _WIN32
|
||||||
@@ -9,7 +9,12 @@
|
|||||||
#include "Loader.h"
|
#include "Loader.h"
|
||||||
#include "MG_Util/Types.h"
|
#include "MG_Util/Types.h"
|
||||||
#include <Config.h>
|
#include <Config.h>
|
||||||
#if !defined(__WIN32) && !defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN 1
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -60,7 +65,14 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void* OpenLib(const Vector<String>& names) {
|
static void* OpenLib(const Vector<String>& names) {
|
||||||
#if !defined(__WIN32) && !defined(_WIN32) && (!defined(__APPLE__) || defined(MOBILEGL_IOS))
|
#if defined(_WIN32)
|
||||||
|
for (const auto& name : names) {
|
||||||
|
if (HMODULE lib = LoadLibraryA(name.c_str())) {
|
||||||
|
MGLOG_I("Loaded GL backend library: %s", name.c_str());
|
||||||
|
return reinterpret_cast<void*>(lib);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#elif !defined(__APPLE__) || defined(MOBILEGL_IOS)
|
||||||
static const String LibPathPrefixes[] = {
|
static const String LibPathPrefixes[] = {
|
||||||
#if defined(MOBILEGL_IOS)
|
#if defined(MOBILEGL_IOS)
|
||||||
"@rpath/", "@executable_path/Frameworks/", "@loader_path/Frameworks/",
|
"@rpath/", "@executable_path/Frameworks/", "@loader_path/Frameworks/",
|
||||||
@@ -104,7 +116,9 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void* ProcAddress(void* lib, const char* name) {
|
inline void* ProcAddress(void* lib, const char* name) {
|
||||||
#if !defined(__WIN32) && !defined(_WIN32) && (!defined(__APPLE__) || defined(MOBILEGL_IOS))
|
#if defined(_WIN32)
|
||||||
|
return reinterpret_cast<void*>(::GetProcAddress(reinterpret_cast<HMODULE>(lib), name));
|
||||||
|
#elif !defined(__APPLE__) || defined(MOBILEGL_IOS)
|
||||||
return dlsym(lib, name);
|
return dlsym(lib, name);
|
||||||
#else
|
#else
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -520,6 +534,15 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
#if defined(MOBILEGL_TRACE_ANGLE_VARIANTS) && defined(__ANDROID__)
|
#if defined(MOBILEGL_TRACE_ANGLE_VARIANTS) && defined(__ANDROID__)
|
||||||
void* angleGlesLib = nullptr;
|
void* angleGlesLib = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(_WIN32)
|
||||||
|
// ANGLE is the GLES provider on Windows regardless of UseAngle(). Preload
|
||||||
|
// libGLESv2.dll so libEGL.dll resolves its dependency from the same directory.
|
||||||
|
if (!OpenLib({"libGLESv2.dll"})) {
|
||||||
|
MGLOG_E("Failed to open ANGLE libGLESv2.dll");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
eglLib = OpenLib({"libEGL.dll"});
|
||||||
|
#else
|
||||||
if (UseAngle()) {
|
if (UseAngle()) {
|
||||||
void* glesLib = OpenLib({"libGLESv2_angle.so"});
|
void* glesLib = OpenLib({"libGLESv2_angle.so"});
|
||||||
if (!glesLib) {
|
if (!glesLib) {
|
||||||
@@ -541,6 +564,7 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
eglLib = OpenLib({"libEGL.so"});
|
eglLib = OpenLib({"libEGL.so"});
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
#endif // !_WIN32
|
||||||
|
|
||||||
if (!eglLib) {
|
if (!eglLib) {
|
||||||
MGLOG_E("Failed to open EGL library");
|
MGLOG_E("Failed to open EGL library");
|
||||||
|
|||||||
Reference in New Issue
Block a user