Files
MobileGL/MobileGL/MG_Impl/WGLImpl/WGLImpl.cpp
T

733 lines
28 KiB
C++

// 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.
// 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<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_ONCE("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_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<std::recursive_mutex> 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<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_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<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_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<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