mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
733 lines
28 KiB
C++
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
|