wined3d relies on `WGL_WINE_query_renderer` to get information about current render. The issue is that EGL driver does not implement this extension, which often leads to troubles for wined3d in trying to apply their quirks based only on a `GL_RENDERER` string. Because VRAM size becomes unknown, wined3d refuses to render due to "lack of memory". This MR solves the issue by implementing `WGL_WINE_query_renderer` for EGL driver.
In EGL there's no extension like `GLX_MESA_query_renderer`, so we have to implement it ourselves.
Some caveats of this implementation:
- Obtaining some attributes involves context switching for direct quries to OpenGL driver. I tried to avoid risk of situation where we might lose current driver context, and from my tests it works, but maybe I’m missing something.
- Getting vendor and device PCI IDs is done through DRM, which is a bit tricky at the moment as we have to go through devfs. This can be made cleaner through libdrm, but I don’t think we should introduce a new dependency just for a few attributes.
- The `GL_NVX_gpu_memory_info` extension is used for VRAM size. It is quite common but not supported everywhere, unfortunately there is no simpler way to get information about VRAM.
- There is no API for `WGL_RENDERER_UNIFIED_MEMORY_ARCHITECTURE_WINE`. Same as for winemac.drv, but it’s not a big problem since it doesn’t seem to be used now.
Some advantages:
- Unlike GLX, we are not tied to Mesa specific extensions, which allows it to work for NVIDIA as well, sparing wined3d from having to guess something based only on the GPU name.
- Unlike the same GLX, we can actually query multiple devices. Although `GLX_MESA_query_renderer` also proclaims this, in fact Mesa does not implement anything other than queries to the current renderer: https://gitlab.freedesktop.org/mesa/mesa/-/blob/32a10ccbdd1e6750c84aac3393dc...
Hope I didn’t miss anything.
Signed-off-by: Vasiliy Stelmachenok ventureo@cachyos.org
-- v3: win32u: Advertise WGL_WINE_query_renderer extension win32u: Add wglQueryRendererStringWINE implementation for EGL driver win32u: Add wglQueryRendererIntegerWINE implementation for EGL driver win32u: Add wglQueryCurrentRendererStringWINE implementation for EGL driver win32u: Add wglQueryCurrentRendererIntegerWINE implementation for EGL driver
From: Vasiliy Stelmachenok ventureo@cachyos.org
Signed-off-by: Vasiliy Stelmachenok ventureo@cachyos.org --- dlls/opengl32/make_opengl | 3 + dlls/opengl32/unix_thunks.c | 4 +- dlls/win32u/opengl.c | 370 +++++++++++++++++++++++++++++++++++ include/wine/opengl_driver.h | 1 + include/wine/wgl.h | 16 +- 5 files changed, 391 insertions(+), 3 deletions(-)
diff --git a/dlls/opengl32/make_opengl b/dlls/opengl32/make_opengl index 25862cd7ee9..a2ae4e514a6 100755 --- a/dlls/opengl32/make_opengl +++ b/dlls/opengl32/make_opengl @@ -890,7 +890,10 @@ my %supported_wgl_extensions = ); my %supported_egl_extensions = ( + "EGL_EXT_device_base" => 1, + "EGL_EXT_device_drm" => 1, "EGL_EXT_pixel_format_float" => 1, + "EGL_EXT_platform_device" => 1, "EGL_EXT_present_opaque" => 1, "EGL_KHR_create_context" => 1, "EGL_KHR_create_context_no_error" => 1, diff --git a/dlls/opengl32/unix_thunks.c b/dlls/opengl32/unix_thunks.c index 7ecf7514afa..5f0c20baedd 100644 --- a/dlls/opengl32/unix_thunks.c +++ b/dlls/opengl32/unix_thunks.c @@ -97533,8 +97533,8 @@ struct opengl_funcs null_opengl_funcs = .p_wglSwapIntervalEXT = null_wglSwapIntervalEXT, };
-const int extension_registry_size = 2694; -const struct registry_entry extension_registry[2694] = +const int extension_registry_size = 2698; +const struct registry_entry extension_registry[2698] = { { "glAccumxOES", "GL_OES_fixed_point", offsetof(struct opengl_funcs, p_glAccumxOES) }, { "glAcquireKeyedMutexWin32EXT", "GL_EXT_win32_keyed_mutex", offsetof(struct opengl_funcs, p_glAcquireKeyedMutexWin32EXT) }, diff --git a/dlls/win32u/opengl.c b/dlls/win32u/opengl.c index 2454daf7e11..34100840ac4 100644 --- a/dlls/win32u/opengl.c +++ b/dlls/win32u/opengl.c @@ -25,6 +25,7 @@
#include <assert.h> #include <pthread.h> +#include <dirent.h> #include <dlfcn.h>
#include "ntstatus.h" @@ -173,6 +174,40 @@ static BOOL opengl_drawable_swap( struct opengl_drawable *drawable )
#ifdef SONAME_LIBEGL
+struct egl_renderer_info +{ + EGLint index; + EGLDisplay display; + EGLDeviceEXT device; + EGLContext context; + EGLint gl_core_version; + EGLint gl_compatibility_version; +}; + +static const struct { int major, minor; } gl_versions[] = +{ + {4, 6}, + {4, 5}, + {4, 4}, + {4, 3}, + {4, 2}, + {4, 1}, + {4, 0}, + {3, 3}, + {3, 2}, + {3, 1}, + {3, 0}, + {2, 1}, + {2, 0}, + {1, 5}, + {1, 4}, + {1, 3}, + {1, 2}, + {1, 1}, + {1, 0}, + {0, 0} +}; + struct framebuffer_surface { struct opengl_drawable base; @@ -624,8 +659,340 @@ static BOOL egldrv_describe_pixel_format( int format, struct wgl_pixel_format *d return describe_egl_config( egl->configs[format % count], desc, onscreen ); }
+static BOOL get_drm_device_pci_id( const char* drm_device_file, const char* type, GLuint* value ) +{ + DIR *dfd; + FILE *file; + char path[PATH_MAX], symlink[PATH_MAX]; + char* pci_id; + int i; + struct dirent *dent; + + dfd = opendir("/dev/dri/by-path"); + if (!dfd) + { + WARN( "Can't open directory /dev/dri/by-path\n" ); + return FALSE; + } + + while((dent = readdir( dfd )) != NULL) + { + if(strcmp( dent->d_name, "." ) == 0 || strcmp( dent->d_name, ".." ) == 0) + continue; + + snprintf( symlink, sizeof(symlink), "/dev/dri/by-path/%s", dent->d_name ); + + if(!realpath( symlink, path )) + continue; + + if (strcmp( path, drm_device_file ) != 0) + continue; + + pci_id = dent->d_name; + for (i = 0; i < strlen( "pci-" ); i++) pci_id++; + for (i = 0; i < strlen( "-card" ); i++) pci_id[strlen( pci_id ) - 1] = 0; + snprintf( path, sizeof( path ), "/sys/bus/pci/devices/%s/%s", pci_id, type ); + file = fopen( path, "r" ); + + if (!file) { + closedir( dfd ); + return FALSE; + } + + fscanf( file, "%x", value ); + fclose( file ); + closedir( dfd ); + return TRUE; + } + + closedir( dfd ); + return FALSE; +} + +static BOOL egl_get_renderer_info( struct egl_renderer_info* renderer ) +{ + EGLDeviceEXT* devices; + EGLContext tmp; + EGLint devices_count, configs_count; + const struct egl_platform *egl = &display_egl; + const struct opengl_funcs *funcs = &display_funcs; + static const int config_attribs[] = { + EGL_ALPHA_SIZE, 1, + EGL_BLUE_SIZE, 1, + EGL_CONFORMANT, EGL_OPENGL_BIT, + EGL_GREEN_SIZE, 1, + EGL_RED_SIZE, 1, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_NONE + }; + EGLConfig configs[1] = { EGL_NO_CONFIG_KHR }; + + /* + * Do not iterate over devices if we query information + * about the default display device. + */ + if (renderer->index != -1) { + funcs->p_eglQueryDevicesEXT( 0, NULL, &devices_count ); + devices = calloc( devices_count, sizeof(EGLDeviceEXT*) ); + funcs->p_eglQueryDevicesEXT( devices_count, devices, &devices_count ); + + if (renderer->index < devices_count) { + renderer->device = devices[renderer->index]; + free(devices); + } + + if (!renderer->device) { + WARN( "Failed to find a renderer %u in EGL devices\n", renderer->index ); + return FALSE; + } + + renderer->display = funcs->p_eglGetPlatformDisplay( EGL_PLATFORM_DEVICE_EXT, renderer->device, NULL ); + + if (renderer->display == EGL_NO_DISPLAY) { + WARN( "Failed to get EGL display for renderer %u (error %#x)\n", renderer->index, funcs->p_eglGetError() ); + return FALSE; + } + + if (renderer->display != egl->display && !funcs->p_eglInitialize( renderer->display, NULL, NULL )) { + WARN( "Failed to initialize EGL display for renderer %u (error %#x)\n", renderer->index, funcs->p_eglGetError() ); + return FALSE; + } + } else { + renderer->display = egl->display; + + if (!funcs->p_eglQueryDisplayAttribEXT( renderer->display, EGL_DEVICE_EXT, (EGLAttrib*) &renderer->device )) { + WARN( "Failed to query EGL display attribute (error %#x).\n", funcs->p_eglGetError() ); + return FALSE; + } + } + + funcs->p_eglBindAPI(EGL_OPENGL_API); + + renderer->gl_compatibility_version = renderer->gl_core_version = 0; + + for (int i = 0; gl_versions[i].major > 2; i++) { + const int context_attribs[] = { + EGL_CONTEXT_MAJOR_VERSION, gl_versions[i].major, + EGL_CONTEXT_MINOR_VERSION, gl_versions[i].minor, + EGL_CONTEXT_OPENGL_PROFILE_MASK, + EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT, + EGL_NONE, + }; + + if ((tmp = funcs->p_eglCreateContext( renderer->display, configs[0], EGL_NO_CONTEXT, context_attribs ))) { + renderer->gl_compatibility_version = (gl_versions[i].major * 10) + gl_versions[i].minor; + renderer->context = tmp; + break; + } + } + + for (int i = 0; gl_versions[i].major > 0; i++) { + const int context_attribs[] = { + EGL_CONTEXT_MAJOR_VERSION, gl_versions[i].major, + EGL_CONTEXT_MINOR_VERSION, gl_versions[i].minor, + EGL_CONTEXT_OPENGL_PROFILE_MASK, + EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + EGL_NONE, + }; + + /* Fallback to config choosing, since EGL_NO_CONFIG_KHR is not supported + * for contexts below OpenGL 3. + */ + if (gl_versions[i].major <= 2) { + if (!funcs->p_eglChooseConfig( renderer->display, config_attribs, configs, 1, &configs_count )) { + WARN( "Failed to choose EGL config for renderer %u (error %#x)\n", renderer->index, funcs->p_eglGetError() ); + break; + } + } + + if ((tmp = funcs->p_eglCreateContext( renderer->display, configs[0], EGL_NO_CONTEXT, context_attribs ))) { + renderer->gl_core_version = (gl_versions[i].major * 10) + gl_versions[i].minor; + renderer->context = tmp; + break; + } + } + + if (!renderer->context) { + WARN( "Failed to create EGL context for renderer %u (error %#x)\n", renderer->index, funcs->p_eglGetError() ); + return FALSE; + } + + return TRUE; +} + +static BOOL egl_query_integer( GLint renderer, GLenum attribute, GLuint *value ) +{ + BOOL ret = TRUE; + struct egl_renderer_info egl_renderer = { + .index = renderer, + .display = NULL, + .context = NULL, + .device = NULL, + .gl_core_version = 0, + .gl_compatibility_version = 0, + }; + const struct opengl_funcs *funcs = &display_funcs; + const struct egl_platform *egl = &display_egl; + struct wgl_context *wgl_context = NtCurrentTeb()->glContext; + + if (!egl_get_renderer_info( &egl_renderer )) { + WARN( "Failed to get info about %d renderer\n", renderer ); + return FALSE; + } + + funcs->p_eglMakeCurrent( egl_renderer.display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_renderer.context ); + + switch (attribute) + { + case WGL_RENDERER_VERSION_WINE: + { + const char* version = (const char*) funcs->p_glGetString( GL_VERSION ); + int count = 0; + + if (version) { + const char* tmp = strrchr( version, ' ' ); + if (!tmp) ret = FALSE; + if ((count = sscanf( tmp, "%u.%u.%u", &value[0], &value[1], &value[2] )) < 2) + ret = FALSE; + if (count < 3) value[2] = 0; + } else { + ret = FALSE; + } + + if (!ret) { + WARN( "Failed to parse renderer version\n" ); + value[0] = value[1] = value[2] = 0; + ret = TRUE; + } + + TRACE( "WGL_RENDERER_VERSION_WINE -> %u.%u.%u\n", value[0], value[1], value[2] ); + break; + } + case WGL_RENDERER_ACCELERATED_WINE: + { + const char* egl_extensions = funcs->p_eglQueryDeviceStringEXT( egl_renderer.device, EGL_EXTENSIONS ); + + /* Assume that all devices without EGL_MESA_device_software are + * accelerated. */ + *value = !has_extension( egl_extensions, "EGL_MESA_device_software" ); + TRACE( "WGL_RENDERER_ACCELERATED_WINE -> %u\n", *value ); + break; + } + case WGL_RENDERER_DEVICE_ID_WINE: + case WGL_RENDERER_VENDOR_ID_WINE: + { + BOOL is_vendor_id = (attribute == WGL_RENDERER_VENDOR_ID_WINE); + const char* drm_device_file; + const char* egl_extensions = funcs->p_eglQueryDeviceStringEXT( egl_renderer.device, EGL_EXTENSIONS ); + + /* EGL does not provide a convenient way to get device / vendor ID, + * so we have to do it manually through DRM. + * Otherwise fallback to value as for SoC devices in GLX */ + if (has_extension( egl_extensions, "EGL_EXT_device_drm" ) && + (drm_device_file = funcs->p_eglQueryDeviceStringEXT( egl_renderer.device, EGL_DRM_DEVICE_FILE_EXT ))) + ret = get_drm_device_pci_id( drm_device_file, is_vendor_id ? "vendor" : "device", value ); + + if (!ret) *value = 0xffffffff; + + TRACE( "%s -> 0x%04x\n", (is_vendor_id ? "WGL_RENDERER_VENDOR_ID_WINE" : "WGL_RENDERER_DEVICE_ID_WINE"), *value ); + break; + } + case WGL_RENDERER_OPENGL_COMPATIBILITY_PROFILE_VERSION_WINE: + { + value[0] = egl_renderer.gl_compatibility_version / 10; + value[1] = egl_renderer.gl_compatibility_version % 10; + TRACE( "WGL_RENDERER_OPENGL_COMPATIBILITY_PROFILE_VERSION_WINE -> %u.%u\n", value[0], value[1] ); + break; + } + case WGL_RENDERER_OPENGL_CORE_PROFILE_VERSION_WINE: + { + value[0] = egl_renderer.gl_core_version / 10; + value[1] = egl_renderer.gl_core_version % 10 ; + TRACE( "WGL_RENDERER_OPENGL_CORE_PROFILE_VERSION_WINE -> %u.%u\n", value[0], value[1] ); + break; + } + case WGL_RENDERER_PREFERRED_PROFILE_WINE: + { + if (egl_renderer.gl_core_version != 0) { + *value = WGL_CONTEXT_CORE_PROFILE_BIT_ARB; + TRACE( "WGL_RENDERER_PREFERRED_PROFILE_WINE -> WGL_CONTEXT_CORE_PROFILE_BIT_ARB (0x%04x)\n", *value ); + } else { + *value = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + TRACE( "WGL_RENDERER_PREFERRED_PROFILE_WINE -> WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB (0x%04x)\n", *value ); + } + break; + } + + case WGL_RENDERER_UNIFIED_MEMORY_ARCHITECTURE_WINE: + { + /* FIXME: Assume that we always have no UMA memory, + * since there is no correct way to check it. */ + *value = 0; + TRACE( "WGL_RENDERER_UNIFIED_MEMORY_ARCHITECTURE_WINE -> %u\n", *value ); + break; + } + case WGL_RENDERER_VIDEO_MEMORY_WINE: + { + GLint memory = 0; + + /* EGL does not provide a platform-independent way to get video + * memory size, so we have to do it through the vector-specific GL + * extensions. */ + if (egl_renderer.gl_core_version >= 30 || egl_renderer.gl_compatibility_version >= 30) { + GLint extensions_count = 0; + funcs->p_glGetIntegerv( GL_NUM_EXTENSIONS, &extensions_count ); + + while (extensions_count--) { + const char* extension = (const char*) funcs->p_glGetStringi( GL_EXTENSIONS, extensions_count ); + + if (strcmp( extension, "GL_NVX_gpu_memory_info" ) == 0) { + funcs->p_glGetIntegerv( GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &memory ); + break; + } + } + } else { + const char* gl_extensions = (const char*) funcs->p_glGetString( GL_EXTENSIONS ); + if (has_extension( gl_extensions, "GL_NVX_gpu_memory_info" )) + funcs->p_glGetIntegerv( GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &memory ); + } + + *value = memory / 1024; + + TRACE( "WGL_RENDERER_VIDEO_MEMORY_WINE -> %uMB\n", *value ); + break; + } + default: + ret = FALSE; + FIXME( "Unrecognized attribute 0x%04x\n", attribute ); + break; + } + + if (egl_renderer.context) + funcs->p_eglDestroyContext( egl_renderer.display, egl_renderer.context ); + + if (egl_renderer.display && egl_renderer.display != egl->display) + funcs->p_eglTerminate( egl_renderer.display ); + + if (wgl_context->driver_private) + funcs->p_eglMakeCurrent( egl->display, wgl_context ? wgl_context->draw->surface : EGL_NO_SURFACE, + wgl_context ? wgl_context->read->surface : EGL_NO_SURFACE, wgl_context->driver_private ); + + return ret; +} + +static BOOL egldrv_wglQueryCurrentRendererIntegerWINE( GLenum attribute, GLuint *value ) +{ + struct wgl_context *wgl_context = NtCurrentTeb()->glContext; + + TRACE( "context %p/%p/%p attribute 0x%04x value %p\n", wgl_context, (wgl_context ? wgl_context->driver_private : NULL), + (wgl_context ? wgl_context->internal_context : NULL), attribute, value ); + + return egl_query_integer( -1, attribute, value ); +} + static const char *egldrv_init_wgl_extensions( struct opengl_funcs *funcs ) { + funcs->p_wglQueryCurrentRendererIntegerWINE = egldrv_wglQueryCurrentRendererIntegerWINE; return ""; }
@@ -881,7 +1248,9 @@ static BOOL egl_init( const struct opengl_driver_funcs **driver_funcs ) goto failed; \ } CHECK_EXTENSION( EGL_KHR_client_get_all_proc_addresses ); + CHECK_EXTENSION( EGL_EXT_device_base ); CHECK_EXTENSION( EGL_EXT_platform_base ); + CHECK_EXTENSION( EGL_EXT_platform_device ); #undef CHECK_EXTENSION
#define USE_GL_FUNC( func ) \ @@ -891,6 +1260,7 @@ static BOOL egl_init( const struct opengl_driver_funcs **driver_funcs ) goto failed; \ } ALL_EGL_FUNCS + ALL_EGL_EXT_FUNCS #undef USE_GL_FUNC
*driver_funcs = &egldrv_funcs; diff --git a/include/wine/opengl_driver.h b/include/wine/opengl_driver.h index 296b030021d..dae81ef0c87 100644 --- a/include/wine/opengl_driver.h +++ b/include/wine/opengl_driver.h @@ -121,6 +121,7 @@ struct opengl_funcs BOOL (*p_wglSwapIntervalEXT)( int interval ); #define USE_GL_FUNC(x) PFN_##x p_##x; ALL_EGL_FUNCS + ALL_EGL_EXT_FUNCS ALL_GL_FUNCS ALL_GL_EXT_FUNCS #undef USE_GL_FUNC diff --git a/include/wine/wgl.h b/include/wine/wgl.h index d7bc3664b73..5cbcb101b99 100644 --- a/include/wine/wgl.h +++ b/include/wine/wgl.h @@ -145,6 +145,7 @@ typedef unsigned int GLhandleARB; #define EGL_BAD_CONFIG 0x3005 #define EGL_BAD_CONTEXT 0x3006 #define EGL_BAD_CURRENT_SURFACE 0x3007 +#define EGL_BAD_DEVICE_EXT 0x322B #define EGL_BAD_DISPLAY 0x3008 #define EGL_BAD_MATCH 0x3009 #define EGL_BAD_NATIVE_PIXMAP 0x300A @@ -196,9 +197,12 @@ typedef unsigned int GLhandleARB; #define EGL_CORE_NATIVE_ENGINE 0x305B #define EGL_DEFAULT_DISPLAY EGL_CAST(EGLNativeDisplayType,0) #define EGL_DEPTH_SIZE 0x3025 +#define EGL_DEVICE_EXT 0x322C #define EGL_DISPLAY_SCALING 10000 #define EGL_DONT_CARE EGL_CAST(EGLint,-1) #define EGL_DRAW 0x3059 +#define EGL_DRM_DEVICE_FILE_EXT 0x3233 +#define EGL_DRM_MASTER_FD_EXT 0x333C #define EGL_EXTENSIONS 0x3055 #define EGL_FALSE 0 #define EGL_FOREVER 0xFFFFFFFFFFFFFFFF @@ -246,6 +250,7 @@ typedef unsigned int GLhandleARB; #define EGL_NOT_INITIALIZED 0x3001 #define EGL_NO_CONFIG_KHR EGL_CAST(EGLConfig,0) #define EGL_NO_CONTEXT EGL_CAST(EGLContext,0) +#define EGL_NO_DEVICE_EXT EGL_CAST(EGLDeviceEXT,0) #define EGL_NO_DISPLAY EGL_CAST(EGLDisplay,0) #define EGL_NO_IMAGE EGL_CAST(EGLImage,0) #define EGL_NO_RESET_NOTIFICATION 0x31BE @@ -267,6 +272,7 @@ typedef unsigned int GLhandleARB; #define EGL_PIXEL_ASPECT_RATIO 0x3092 #define EGL_PIXMAP_BIT 0x0002 #define EGL_PLATFORM_ANDROID_KHR 0x3141 +#define EGL_PLATFORM_DEVICE_EXT 0x313F #define EGL_PLATFORM_SURFACELESS_MESA 0x31DD #define EGL_PLATFORM_WAYLAND_KHR 0x31D8 #define EGL_PLATFORM_X11_KHR 0x31D5 @@ -6234,6 +6240,10 @@ typedef void (GLAPIENTRY *PFN_glVertex4s)( GLshort x, GLshort y, GLshort z typedef void (GLAPIENTRY *PFN_glVertex4sv)( const GLshort *v ); typedef void (GLAPIENTRY *PFN_glVertexPointer)( GLint size, GLenum type, GLsizei stride, const void *pointer ); typedef void (GLAPIENTRY *PFN_glViewport)( GLint x, GLint y, GLsizei width, GLsizei height ); +typedef EGLBoolean (GLAPIENTRY *PFN_eglQueryDeviceAttribEXT)( EGLDeviceEXT device, EGLint attribute, EGLAttrib *value ); +typedef const char * (GLAPIENTRY *PFN_eglQueryDeviceStringEXT)( EGLDeviceEXT device, EGLint name ); +typedef EGLBoolean (GLAPIENTRY *PFN_eglQueryDevicesEXT)( EGLint max_devices, EGLDeviceEXT *devices, EGLint *num_devices ); +typedef EGLBoolean (GLAPIENTRY *PFN_eglQueryDisplayAttribEXT)( EGLDisplay dpy, EGLint attribute, EGLAttrib *value ); typedef void (GLAPIENTRY *PFN_glAccumxOES)( GLenum op, GLfixed value ); typedef GLboolean (GLAPIENTRY *PFN_glAcquireKeyedMutexWin32EXT)( GLuint memory, GLuint64 key, GLuint timeout ); typedef void (GLAPIENTRY *PFN_glActiveProgramEXT)( GLuint program ); @@ -9028,7 +9038,11 @@ typedef BOOL (GLAPIENTRY *PFN_wglSwapIntervalEXT)( int interval ); USE_GL_FUNC(eglWaitNative) \ USE_GL_FUNC(eglWaitSync)
-#define ALL_EGL_EXT_FUNCS +#define ALL_EGL_EXT_FUNCS \ + USE_GL_FUNC(eglQueryDeviceAttribEXT) \ + USE_GL_FUNC(eglQueryDeviceStringEXT) \ + USE_GL_FUNC(eglQueryDevicesEXT) \ + USE_GL_FUNC(eglQueryDisplayAttribEXT)
#define ALL_GL_FUNCS \ USE_GL_FUNC(glAccum) \
From: Vasiliy Stelmachenok ventureo@cachyos.org
Signed-off-by: Vasiliy Stelmachenok ventureo@cachyos.org --- dlls/win32u/opengl.c | 57 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+)
diff --git a/dlls/win32u/opengl.c b/dlls/win32u/opengl.c index 34100840ac4..1f0e4dfefd8 100644 --- a/dlls/win32u/opengl.c +++ b/dlls/win32u/opengl.c @@ -980,6 +980,52 @@ static BOOL egl_query_integer( GLint renderer, GLenum attribute, GLuint *value ) return ret; }
+static const char *egl_query_string( GLint renderer, GLenum attribute ) +{ + const char* ret = NULL; + struct egl_renderer_info egl_renderer = { + .index = renderer, + .display = NULL, + .context = NULL, + .device = NULL, + .gl_core_version = 0, + .gl_compatibility_version = 0, + }; + const struct egl_platform *egl = &display_egl; + const struct opengl_funcs *funcs = &display_funcs; + struct wgl_context *wgl_context = NtCurrentTeb()->glContext; + + if (!egl_get_renderer_info( &egl_renderer )) { + return ret; + } + + funcs->p_eglMakeCurrent( egl_renderer.display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_renderer.context ); + + switch (attribute) + { + case WGL_RENDERER_DEVICE_ID_WINE: + ret = (const char*) funcs->p_glGetString( GL_RENDERER ); + TRACE( "WGL_RENDERER_DEVICE_ID_WINE -> %s\n", debugstr_a( ret ) ); + break; + case WGL_RENDERER_VENDOR_ID_WINE: + ret = (const char*) funcs->p_glGetString( GL_VENDOR ); + TRACE( "WGL_RENDERER_VENDOR_ID_WINE -> %s\n", debugstr_a( ret ) ); + break; + default: + FIXME( "Unrecognized attribute 0x%04x\n", attribute ); + break; + } + + if (egl_renderer.context) + funcs->p_eglDestroyContext( egl_renderer.display, egl_renderer.context ); + + if (wgl_context->driver_private) + funcs->p_eglMakeCurrent( egl->display, wgl_context ? wgl_context->draw->surface : EGL_NO_SURFACE, + wgl_context ? wgl_context->read->surface : EGL_NO_SURFACE, wgl_context->driver_private ); + + return ret; +} + static BOOL egldrv_wglQueryCurrentRendererIntegerWINE( GLenum attribute, GLuint *value ) { struct wgl_context *wgl_context = NtCurrentTeb()->glContext; @@ -990,9 +1036,20 @@ static BOOL egldrv_wglQueryCurrentRendererIntegerWINE( GLenum attribute, GLuint return egl_query_integer( -1, attribute, value ); }
+static const char *egldrv_wglQueryCurrentRendererStringWINE( GLenum attribute ) +{ + struct wgl_context *context = NtCurrentTeb()->glContext; + + TRACE("context %p/%p/%p attribute 0x%04x\n", context, (context ? context->driver_private : NULL), + (context ? context->internal_context : NULL), attribute); + + return egl_query_string( -1, attribute ); +} + static const char *egldrv_init_wgl_extensions( struct opengl_funcs *funcs ) { funcs->p_wglQueryCurrentRendererIntegerWINE = egldrv_wglQueryCurrentRendererIntegerWINE; + funcs->p_wglQueryCurrentRendererStringWINE = egldrv_wglQueryCurrentRendererStringWINE; return ""; }
From: Vasiliy Stelmachenok ventureo@cachyos.org
Signed-off-by: Vasiliy Stelmachenok ventureo@cachyos.org --- dlls/win32u/opengl.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/dlls/win32u/opengl.c b/dlls/win32u/opengl.c index 1f0e4dfefd8..6a6946510fb 100644 --- a/dlls/win32u/opengl.c +++ b/dlls/win32u/opengl.c @@ -1046,10 +1046,21 @@ static const char *egldrv_wglQueryCurrentRendererStringWINE( GLenum attribute ) return egl_query_string( -1, attribute ); }
+static BOOL egldrv_wglQueryRendererIntegerWINE( HDC dc, GLint renderer, GLenum attribute, GLuint *value ) +{ + struct wgl_context *wgl_context = NtCurrentTeb()->glContext; + + TRACE( "context %p/%p/%p attribute 0x%04x value %p\n", wgl_context, (wgl_context ? wgl_context->driver_private : NULL), + (wgl_context ? wgl_context->internal_context : NULL), attribute, value ); + + return egl_query_integer( renderer, attribute, value ); +} + static const char *egldrv_init_wgl_extensions( struct opengl_funcs *funcs ) { funcs->p_wglQueryCurrentRendererIntegerWINE = egldrv_wglQueryCurrentRendererIntegerWINE; funcs->p_wglQueryCurrentRendererStringWINE = egldrv_wglQueryCurrentRendererStringWINE; + funcs->p_wglQueryRendererIntegerWINE = egldrv_wglQueryRendererIntegerWINE; return ""; }
From: Vasiliy Stelmachenok ventureo@cachyos.org
Signed-off-by: Vasiliy Stelmachenok ventureo@cachyos.org --- dlls/win32u/opengl.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/dlls/win32u/opengl.c b/dlls/win32u/opengl.c index 6a6946510fb..68311669ced 100644 --- a/dlls/win32u/opengl.c +++ b/dlls/win32u/opengl.c @@ -1056,11 +1056,22 @@ static BOOL egldrv_wglQueryRendererIntegerWINE( HDC dc, GLint renderer, GLenum a return egl_query_integer( renderer, attribute, value ); }
+static const char *egldrv_wglQueryRendererStringWINE( HDC dc, GLint renderer, GLenum attribute ) +{ + struct wgl_context *wgl_context = NtCurrentTeb()->glContext; + + TRACE( "context %p/%p/%p attribute 0x%04x\n", wgl_context, (wgl_context ? wgl_context->driver_private : NULL), + (wgl_context ? wgl_context->internal_context : NULL), attribute ); + + return egl_query_string( renderer, attribute ); +} + static const char *egldrv_init_wgl_extensions( struct opengl_funcs *funcs ) { funcs->p_wglQueryCurrentRendererIntegerWINE = egldrv_wglQueryCurrentRendererIntegerWINE; funcs->p_wglQueryCurrentRendererStringWINE = egldrv_wglQueryCurrentRendererStringWINE; funcs->p_wglQueryRendererIntegerWINE = egldrv_wglQueryRendererIntegerWINE; + funcs->p_wglQueryRendererStringWINE = egldrv_wglQueryRendererStringWINE; return ""; }
From: Vasiliy Stelmachenok ventureo@cachyos.org
Signed-off-by: Vasiliy Stelmachenok ventureo@cachyos.org --- dlls/win32u/opengl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/dlls/win32u/opengl.c b/dlls/win32u/opengl.c index 68311669ced..006ee3dc4b9 100644 --- a/dlls/win32u/opengl.c +++ b/dlls/win32u/opengl.c @@ -1068,11 +1068,13 @@ static const char *egldrv_wglQueryRendererStringWINE( HDC dc, GLint renderer, GL
static const char *egldrv_init_wgl_extensions( struct opengl_funcs *funcs ) { + static char egldrv_wgl_extensions[4096]; funcs->p_wglQueryCurrentRendererIntegerWINE = egldrv_wglQueryCurrentRendererIntegerWINE; funcs->p_wglQueryCurrentRendererStringWINE = egldrv_wglQueryCurrentRendererStringWINE; funcs->p_wglQueryRendererIntegerWINE = egldrv_wglQueryRendererIntegerWINE; funcs->p_wglQueryRendererStringWINE = egldrv_wglQueryRendererStringWINE; - return ""; + register_extension( egldrv_wgl_extensions, ARRAY_SIZE(egldrv_wgl_extensions), "WGL_WINE_query_renderer" ); + return egldrv_wgl_extensions; }
static BOOL egldrv_surface_create( HWND hwnd, int format, struct opengl_drawable **drawable )
v3: Do not assume that default device is zero-indexed. Instead, query current device through the default EGL display via `eglQueryDisplayAttribEXT`.
Looks good feature wise but I'd instead implement it on top of the existing EGL platforms. What do you think of https://gitlab.winehq.org/wine/wine/-/merge_requests/9132?
On Wed Oct 8 12:03:53 2025 +0000, Rémi Bernon wrote:
Looks good feature wise but I'd instead implement it on top of the existing EGL platforms. What do you think of https://gitlab.winehq.org/wine/wine/-/merge_requests/9132?
Looks good, so I'm closing this MR in favor of yours.
This merge request was closed by Vasiliy Stelmachenok.