gdkglcontext-win32-wgl.c: Fix using wglChoosePixelFormatARB()

If we are querying the best supported pixel format for our HDC via
wglChoosePixelFormatARB() (i.e. we have the WGL_ARB_pixel_format extension),
it may return a pixel format that is different from the pixel format that we
used for the dummy context that we have setup, in order to, well, run
wglChoosePixelFormatARB(), which sadly requires a WGL context (HGLRC) to be
current in order to use it, which means the dummy HDC already has a pixel
format that has been set (notice that each HDC is only allowed to have its
pixel format to be set *once*). This is notably the case on Intel display
drivers.

Since we are emulating surfaceless GL contexts, we are using the dummy GL
context (and thus dummy HDC that is derived from the notification HWND used in
GdkWin32Display) for doing that, we would get into trouble if th actual HDC
from the GdkWin32Surface has a different pixel format set.

So, as a result, in order to fix this situation, we do the following:

* Create yet another dummy HWND in order to grab the HDC to query for the
  capabilities the GL drivers support, and to call wglChoosePixelFormatARB() as
  appropriate (or ChoosePixelFormat()) for the final pixel format that we use.
* Ditch the dummy GL context, HDC and HWND after obtaining the pixel format.
* Then set the final pixel format that we obtained onto the HDC that is derived
  from the HWND used in GdkWin32Display for notifications, which will become our
  new dummy HDC.
* Create a new dummy HGLRC for use with the new dummy HDC to emulate surfaceless
  GL support.
This commit is contained in:
Chun-wei Fan 2023-05-03 17:58:54 +08:00
parent 347561fa68
commit 50ef36d387
3 changed files with 121 additions and 35 deletions

View File

@ -646,8 +646,6 @@ gdk_win32_display_dispose (GObject *object)
{
GdkWin32Display *display_win32 = GDK_WIN32_DISPLAY (object);
if (display_win32->hwnd != NULL)
{
if (display_win32->dummy_context_wgl.hglrc != NULL)
{
wglMakeCurrent (NULL, NULL);
@ -655,6 +653,8 @@ gdk_win32_display_dispose (GObject *object)
display_win32->dummy_context_wgl.hglrc = NULL;
}
if (display_win32->hwnd != NULL)
{
DestroyWindow (display_win32->hwnd);
display_win32->hwnd = NULL;
}
@ -1129,7 +1129,6 @@ gdk_win32_display_get_monitor_scale_factor (GdkWin32Display *display_win32,
{
if (GDK_WIN32_SURFACE (surface)->hdc == NULL)
GDK_WIN32_SURFACE (surface)->hdc = GetDC (GDK_SURFACE_HWND (surface));
hdc = GDK_WIN32_SURFACE (surface)->hdc;
}
else
@ -1184,11 +1183,6 @@ gdk_win32_display_init_gl (GdkDisplay *display,
HDC init_gl_hdc = NULL;
GdkGLContext *context;
if (display_win32->dummy_context_wgl.hdc == NULL)
display_win32->dummy_context_wgl.hdc = GetDC (display_win32->hwnd);
init_gl_hdc = display_win32->dummy_context_wgl.hdc;
/*
* No env vars set, do the regular GL initialization, first WGL and then EGL,
* as WGL is the more tried-and-tested configuration.
@ -1201,6 +1195,8 @@ gdk_win32_display_init_gl (GdkDisplay *display,
*/
if (gdk_display_get_debug_flags (display) & (GDK_DEBUG_GL_EGL|GDK_DEBUG_GL_GLES))
{
init_gl_hdc = GetDC (display_win32->hwnd);
if (gdk_display_init_egl (display,
EGL_PLATFORM_ANGLE_ANGLE,
init_gl_hdc,
@ -1218,12 +1214,14 @@ gdk_win32_display_init_gl (GdkDisplay *display,
#endif
context = gdk_win32_display_init_wgl (display, error);
if (context)
return context;
#ifdef HAVE_EGL
g_clear_error (error);
init_gl_hdc = GetDC (display_win32->hwnd);
if (gdk_display_init_egl (display,
EGL_PLATFORM_ANGLE_ANGLE,
init_gl_hdc,

View File

@ -108,6 +108,7 @@ typedef enum {
typedef struct
{
HWND hwnd;
HDC hdc;
HGLRC hglrc;
} GdkWin32GLDummyContextWGL;

View File

@ -140,7 +140,8 @@ get_wgl_pfd (HDC hdc,
pfd->nSize = sizeof (PIXELFORMATDESCRIPTOR);
if (display_win32 != NULL && display_win32->hasWglARBPixelFormat)
if (display_win32 != NULL &&
display_win32->hasWglARBPixelFormat)
{
UINT num_formats;
int colorbits = GetDeviceCaps (hdc, BITSPIXEL);
@ -247,6 +248,43 @@ gdk_init_dummy_wgl_context (GdkWin32Display *display_win32)
return best_idx;
}
/*
* Use a dummy HWND to init GL, sadly we can't just use the
* HWND that we use for notifications as we may only call
* SetPixelFormat() on an HDC once, and that notification HWND
* uses the CS_OWNDC style meaning that even if we were to call
* DeleteDC() on it, we would get the exact same HDC when we call
* GetDC() on it later, meaning SetPixelFormat() cannot be used
* again on the HDC that we acquire from the notification HWND.
*/
static HWND
create_dummy_gl_window (void)
{
WNDCLASS wclass = { 0, };
ATOM klass;
HWND hwnd;
wclass.lpszClassName = "GdkGLDummyWindow";
wclass.lpfnWndProc = DefWindowProc;
wclass.hInstance = _gdk_app_hmodule;
wclass.style = CS_OWNDC;
klass = RegisterClass (&wclass);
if (klass)
{
hwnd = CreateWindow (MAKEINTRESOURCE (klass),
NULL, WS_POPUP,
0, 0, 0, 0, NULL, NULL,
_gdk_app_hmodule, NULL);
if (!hwnd)
{
UnregisterClass (MAKEINTRESOURCE (klass), _gdk_app_hmodule);
}
}
return hwnd;
}
GdkGLContext *
gdk_win32_display_init_wgl (GdkDisplay *display,
GError **error)
@ -263,6 +301,15 @@ gdk_win32_display_init_wgl (GdkDisplay *display,
* dummy GL Context, it is used to query functions
* and used for other stuff as well
*/
if (display_win32->dummy_context_wgl.hdc == NULL)
{
display_win32->dummy_context_wgl.hwnd = create_dummy_gl_window ();
if (display_win32->dummy_context_wgl.hwnd != NULL)
display_win32->dummy_context_wgl.hdc = GetDC (display_win32->dummy_context_wgl.hwnd);
}
best_idx = gdk_init_dummy_wgl_context (display_win32);
hdc = display_win32->dummy_context_wgl.hdc;
@ -521,34 +568,50 @@ create_wgl_context (GdkGLContext *context,
}
static gboolean
set_wgl_pixformat_for_hdc (HDC hdc,
set_wgl_pixformat_for_hdc (GdkWin32Display *display_win32,
HDC *hdc,
int *best_idx,
GdkWin32Display *display_win32)
gboolean *recreate_dummy_context)
{
gboolean already_checked = TRUE;
*best_idx = GetPixelFormat (hdc);
gboolean skip_acquire = FALSE;
gboolean set_pixel_format_result = FALSE;
PIXELFORMATDESCRIPTOR pfd;
/* one is only allowed to call SetPixelFormat(), and so ChoosePixelFormat()
* one single time per window HDC
*/
if (*best_idx == 0)
{
PIXELFORMATDESCRIPTOR pfd;
gboolean set_pixel_format_result = FALSE;
GDK_NOTE (OPENGL, g_print ("requesting pixel format...\n"));
already_checked = FALSE;
*best_idx = get_wgl_pfd (hdc, &pfd, display_win32);
*best_idx = get_wgl_pfd (*hdc, &pfd, display_win32);
if (*best_idx != 0)
set_pixel_format_result = SetPixelFormat (hdc, *best_idx, &pfd);
if (display_win32->dummy_context_wgl.hwnd != NULL)
{
/*
* Ditch the initial dummy HDC, HGLRC and HWND used to initialize WGL,
* we want to ensure that the HDC of the notification HWND that we will
* also use for our new dummy HDC will have the correct pixel format set
*/
wglDeleteContext (display_win32->dummy_context_wgl.hglrc);
display_win32->dummy_context_wgl.hdc = GetDC (display_win32->hwnd);
*hdc = display_win32->dummy_context_wgl.hdc;
*recreate_dummy_context = TRUE;
DestroyWindow (display_win32->dummy_context_wgl.hwnd);
display_win32->dummy_context_wgl.hwnd = NULL;
}
if (GetPixelFormat (*hdc) != 0)
{
skip_acquire = TRUE;
set_pixel_format_result = TRUE;
}
else if (*best_idx != 0)
set_pixel_format_result = SetPixelFormat (*hdc, *best_idx, &pfd);
/* ChoosePixelFormat() or SetPixelFormat() failed, bail out */
if (*best_idx == 0 || !set_pixel_format_result)
return FALSE;
}
GDK_NOTE (OPENGL, g_print ("%s""requested and set pixel format: %d\n", already_checked ? "already " : "", *best_idx));
GDK_NOTE (OPENGL, g_print ("%s""requested and set pixel format: %d\n", skip_acquire ? "already " : "", *best_idx));
return TRUE;
}
@ -564,8 +627,9 @@ gdk_win32_gl_context_wgl_realize (GdkGLContext *context,
/* request flags and specific versions for core (3.2+) WGL context */
int flags = 0;
HGLRC hglrc;
int pixel_format;
int pixel_format = 0;
HDC hdc;
gboolean recreate_dummy_context = FALSE;
GdkSurface *surface = gdk_gl_context_get_surface (context);
GdkDisplay *display = gdk_gl_context_get_display (context);
@ -591,9 +655,10 @@ gdk_win32_gl_context_wgl_realize (GdkGLContext *context,
else
hdc = display_win32->dummy_context_wgl.hdc;
if (!set_wgl_pixformat_for_hdc (hdc,
if (!set_wgl_pixformat_for_hdc (display_win32,
&hdc,
&pixel_format,
display_win32))
&recreate_dummy_context))
{
g_set_error_literal (error, GDK_GL_ERROR,
GDK_GL_ERROR_UNSUPPORTED_FORMAT,
@ -617,6 +682,28 @@ gdk_win32_gl_context_wgl_realize (GdkGLContext *context,
flags,
legacy_bit,
error);
if (recreate_dummy_context)
{
display_win32->dummy_context_wgl.hglrc =
create_wgl_context (context,
display_win32->dummy_context_wgl.hdc,
display_win32->hasWglARBCreateContext,
NULL,
flags,
legacy_bit,
error);
if (display_win32->dummy_context_wgl.hglrc == NULL)
{
if (hglrc != NULL)
{
wglDeleteContext (hglrc);
hglrc = NULL;
}
}
}
if (hglrc == NULL)
return 0;