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

View File

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

View File

@ -140,7 +140,8 @@ get_wgl_pfd (HDC hdc,
pfd->nSize = sizeof (PIXELFORMATDESCRIPTOR); pfd->nSize = sizeof (PIXELFORMATDESCRIPTOR);
if (display_win32 != NULL && display_win32->hasWglARBPixelFormat) if (display_win32 != NULL &&
display_win32->hasWglARBPixelFormat)
{ {
UINT num_formats; UINT num_formats;
int colorbits = GetDeviceCaps (hdc, BITSPIXEL); int colorbits = GetDeviceCaps (hdc, BITSPIXEL);
@ -247,6 +248,43 @@ gdk_init_dummy_wgl_context (GdkWin32Display *display_win32)
return best_idx; 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 * GdkGLContext *
gdk_win32_display_init_wgl (GdkDisplay *display, gdk_win32_display_init_wgl (GdkDisplay *display,
GError **error) GError **error)
@ -263,6 +301,15 @@ gdk_win32_display_init_wgl (GdkDisplay *display,
* dummy GL Context, it is used to query functions * dummy GL Context, it is used to query functions
* and used for other stuff as well * 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); best_idx = gdk_init_dummy_wgl_context (display_win32);
hdc = display_win32->dummy_context_wgl.hdc; hdc = display_win32->dummy_context_wgl.hdc;
@ -521,34 +568,50 @@ create_wgl_context (GdkGLContext *context,
} }
static gboolean static gboolean
set_wgl_pixformat_for_hdc (HDC hdc, set_wgl_pixformat_for_hdc (GdkWin32Display *display_win32,
HDC *hdc,
int *best_idx, int *best_idx,
GdkWin32Display *display_win32) gboolean *recreate_dummy_context)
{ {
gboolean already_checked = TRUE; gboolean skip_acquire = FALSE;
*best_idx = GetPixelFormat (hdc); gboolean set_pixel_format_result = FALSE;
PIXELFORMATDESCRIPTOR pfd;
/* one is only allowed to call SetPixelFormat(), and so ChoosePixelFormat() /* one is only allowed to call SetPixelFormat(), and so ChoosePixelFormat()
* one single time per window HDC * one single time per window HDC
*/ */
if (*best_idx == 0) GDK_NOTE (OPENGL, g_print ("requesting pixel format...\n"));
*best_idx = get_wgl_pfd (*hdc, &pfd, display_win32);
if (display_win32->dummy_context_wgl.hwnd != NULL)
{ {
PIXELFORMATDESCRIPTOR pfd; /*
gboolean set_pixel_format_result = FALSE; * 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;
GDK_NOTE (OPENGL, g_print ("requesting pixel format...\n")); DestroyWindow (display_win32->dummy_context_wgl.hwnd);
already_checked = FALSE; display_win32->dummy_context_wgl.hwnd = NULL;
*best_idx = get_wgl_pfd (hdc, &pfd, display_win32);
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)); 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", skip_acquire ? "already " : "", *best_idx));
return TRUE; 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 */ /* request flags and specific versions for core (3.2+) WGL context */
int flags = 0; int flags = 0;
HGLRC hglrc; HGLRC hglrc;
int pixel_format; int pixel_format = 0;
HDC hdc; HDC hdc;
gboolean recreate_dummy_context = FALSE;
GdkSurface *surface = gdk_gl_context_get_surface (context); GdkSurface *surface = gdk_gl_context_get_surface (context);
GdkDisplay *display = gdk_gl_context_get_display (context); GdkDisplay *display = gdk_gl_context_get_display (context);
@ -591,9 +655,10 @@ gdk_win32_gl_context_wgl_realize (GdkGLContext *context,
else else
hdc = display_win32->dummy_context_wgl.hdc; 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, &pixel_format,
display_win32)) &recreate_dummy_context))
{ {
g_set_error_literal (error, GDK_GL_ERROR, g_set_error_literal (error, GDK_GL_ERROR,
GDK_GL_ERROR_UNSUPPORTED_FORMAT, GDK_GL_ERROR_UNSUPPORTED_FORMAT,
@ -617,6 +682,28 @@ gdk_win32_gl_context_wgl_realize (GdkGLContext *context,
flags, flags,
legacy_bit, legacy_bit,
error); 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) if (hglrc == NULL)
return 0; return 0;