From: Paul Gofman Subject: [PATCH v2 2/2] winex11.drv: Prioritize smaller depth formats when zero depth is requested in X11DRV_wglChoosePixelFormatARB(). Message-Id: <20210224140453.527295-2-pgofman@codeweavers.com> Date: Wed, 24 Feb 2021 17:04:53 +0300 In-Reply-To: <20210224140453.527295-1-pgofman@codeweavers.com> References: <20210224140453.527295-1-pgofman@codeweavers.com> Fixes Ancient Cities' black screen on Nvidia. Signed-off-by: Paul Gofman --- v2: - use qsort(). dlls/opengl32/tests/opengl.c | 57 +++++++++++++++++ dlls/winex11.drv/opengl.c | 116 +++++++++++++++++++++++++---------- 2 files changed, 142 insertions(+), 31 deletions(-) diff --git a/dlls/opengl32/tests/opengl.c b/dlls/opengl32/tests/opengl.c index e93636ed75e..314fa11225d 100644 --- a/dlls/opengl32/tests/opengl.c +++ b/dlls/opengl32/tests/opengl.c @@ -1764,6 +1764,62 @@ static void test_swap_control(HDC oldhdc) wglMakeCurrent(oldhdc, oldctx); } +static void test_wglChoosePixelFormatARB(HDC hdc) +{ + static int attrib_list[] = + { + WGL_DRAW_TO_WINDOW_ARB, 1, + WGL_SUPPORT_OPENGL_ARB, 1, + 0 + }; + + PIXELFORMATDESCRIPTOR fmt, last_fmt; + BYTE depth, last_depth; + UINT format_count; + int formats[1024]; + unsigned int i; + int res; + + if (!pwglChoosePixelFormatARB) + { + skip("wglChoosePixelFormatARB is not available\n"); + return; + } + + format_count = 0; + res = pwglChoosePixelFormatARB(hdc, attrib_list, NULL, ARRAY_SIZE(formats), formats, &format_count); + ok(res, "Got unexpected result %d.\n", res); + + memset(&last_fmt, 0, sizeof(last_fmt)); + last_depth = 0; + + for (i = 0; i < format_count; ++i) + { + memset(&fmt, 0, sizeof(fmt)); + if (!DescribePixelFormat(hdc, formats[i], sizeof(fmt), &fmt) + || (fmt.dwFlags & PFD_GENERIC_FORMAT)) + { + memset(&fmt, 0, sizeof(fmt)); + continue; + } + + depth = fmt.cDepthBits; + fmt.cDepthBits = 0; + fmt.cStencilBits = 0; + + if (memcmp(&fmt, &last_fmt, sizeof(fmt))) + { + last_fmt = fmt; + last_depth = depth; + } + else + { + ok(last_depth <= depth, "Got unexpected depth %u, last_depth %u, i %u, format %u.\n", + depth, last_depth, i, formats[i]); + } + } +} + START_TEST(opengl) { HWND hwnd; @@ -1858,6 +1914,7 @@ START_TEST(opengl) } test_choosepixelformat(); + test_wglChoosePixelFormatARB(hdc); test_debug_message_callback(); test_setpixelformat(hdc); test_destroy(hdc); diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index e0e5849f010..e35f901bd8d 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -40,6 +40,7 @@ #include "x11drv.h" #include "xcomposite.h" #include "winternl.h" +#include "wine/heap.h" #include "wine/debug.h" #ifdef SONAME_LIBGL @@ -2559,6 +2560,37 @@ static BOOL X11DRV_wglSetPbufferAttribARB( struct wgl_pbuffer *object, const int return ret; } +struct choose_pixel_format_arb_format +{ + int format; + int original_index; + PIXELFORMATDESCRIPTOR pfd; + int depth, stencil; +}; + +static int compare_formats(const void *a, const void *b) +{ + /* Order formats so that onscreen formats go first. Then, if no depth bits requested, + * prioritize formats with smaller depth within the original sort order with respect to + * other attributes. */ + const struct choose_pixel_format_arb_format *fmt_a = a, *fmt_b = b; + BOOL offscreen_a, offscreen_b; + + offscreen_a = fmt_a->format > nb_onscreen_formats; + offscreen_b = fmt_b->format > nb_onscreen_formats; + + if (offscreen_a != offscreen_b) + return offscreen_a - offscreen_b; + if (memcmp(&fmt_a->pfd, &fmt_b->pfd, sizeof(fmt_a->pfd))) + return fmt_a->original_index - fmt_b->original_index; + if (fmt_a->depth != fmt_b->depth) + return fmt_a->depth - fmt_b->depth; + if (fmt_a->stencil != fmt_b->stencil) + return fmt_a->stencil - fmt_b->stencil; + + return fmt_a->original_index - fmt_b->original_index; +} + /** * X11DRV_wglChoosePixelFormatARB * @@ -2567,17 +2599,15 @@ static BOOL X11DRV_wglSetPbufferAttribARB( struct wgl_pbuffer *object, const int static BOOL X11DRV_wglChoosePixelFormatARB( HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats ) { + struct choose_pixel_format_arb_format *formats; + int it, i, format_count; + BYTE depth_bits = 0; + GLXFBConfig* cfgs; + DWORD dwFlags = 0; int attribs[256]; int nAttribs = 0; - GLXFBConfig* cfgs; int nCfgs = 0; - int it; int fmt_id; - int start, end; - UINT pfmt_it = 0; - int run; - int i; - DWORD dwFlags = 0; TRACE("(%p, %p, %p, %d, %p, %p): hackish\n", hdc, piAttribIList, pfAttribFList, nMaxFormats, piFormats, nNumFormats); if (NULL != pfAttribFList) { @@ -2621,6 +2651,10 @@ static BOOL X11DRV_wglChoosePixelFormatARB( HDC hdc, const int *piAttribIList, c if(piAttribIList[i+1]) dwFlags |= PFD_SUPPORT_GDI; break; + case WGL_DEPTH_BITS_ARB: + depth_bits = piAttribIList[i+1]; + break; + } } @@ -2631,37 +2665,57 @@ static BOOL X11DRV_wglChoosePixelFormatARB( HDC hdc, const int *piAttribIList, c return GL_FALSE; } - /* Loop through all matching formats and check if they are suitable. - * Note that this function should at max return nMaxFormats different formats */ - for(run=0; run < 2; run++) + if (!(formats = heap_alloc(nCfgs * sizeof(*formats)))) + { + ERR("No memory.\n"); + XFree(cfgs); + return GL_FALSE; + } + + format_count = 0; + for (it = 0; it < nCfgs; ++it) { - for (it = 0; it < nCfgs && pfmt_it < nMaxFormats; ++it) + struct choose_pixel_format_arb_format *format; + + if (pglXGetFBConfigAttrib(gdi_display, cfgs[it], GLX_FBCONFIG_ID, &fmt_id)) { - if (pglXGetFBConfigAttrib(gdi_display, cfgs[it], GLX_FBCONFIG_ID, &fmt_id)) - { - ERR("Failed to retrieve FBCONFIG_ID from GLXFBConfig, expect problems.\n"); - continue; - } + ERR("Failed to retrieve FBCONFIG_ID from GLXFBConfig, expect problems.\n"); + continue; + } - /* During the first run we only want onscreen formats and during the second only offscreen */ - start = run == 1 ? nb_onscreen_formats : 0; - end = run == 1 ? nb_pixel_formats : nb_onscreen_formats; + for (i = 0; i < nb_pixel_formats; ++i) + if (pixel_formats[i].fmt_id == fmt_id) + break; - for (i = start; i < end; i++) - { - if (pixel_formats[i].fmt_id == fmt_id && (pixel_formats[i].dwFlags & dwFlags) == dwFlags) - { - piFormats[pfmt_it++] = i + 1; - TRACE("at %d/%d found FBCONFIG_ID 0x%x (%d)\n", - it + 1, nCfgs, fmt_id, i + 1); - break; - } - } + if (i == nb_pixel_formats) + continue; + + format = &formats[format_count]; + format->format = i + 1; + format->original_index = it; + + memset(&format->pfd, 0, sizeof(format->pfd)); + if (!describe_pixel_format(format->format, &format->pfd, TRUE)) + ERR("describe_pixel_format failed, format %d.\n", format->format); + + format->depth = format->pfd.cDepthBits; + format->stencil = format->pfd.cStencilBits; + if (!depth_bits && !(format->pfd.dwFlags & PFD_GENERIC_FORMAT)) + { + format->pfd.cDepthBits = 0; + format->pfd.cStencilBits = 0; } + + ++format_count; } - *nNumFormats = pfmt_it; - /** free list */ + qsort(formats, format_count, sizeof(*formats), compare_formats); + + *nNumFormats = min(nMaxFormats, format_count); + for (i = 0; i < *nNumFormats; ++i) + piFormats[i] = formats[i].format; + + heap_free(formats); XFree(cfgs); return GL_TRUE; } -- 2.29.2