Instead of https://gitlab.winehq.org/wine/wine/-/merge_requests/8496, leaving context sharing for later.
-- v4: win32u: Remove now unnecessary opengl_drawable hdc member. winewayland: Use a distinct wayland_pbuffer struct for pbuffers. win32u: Implement generic EGL driver pbuffer surface. win32u: Avoid releasing opengl drawable within drawables_lock. win32u: Avoid calling detach, flush or swap on pbuffer drawables. winemac: Remove now unnecessary pbuffer tracking.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winemac.drv/opengl.c | 77 ++++++--------------------------------- 1 file changed, 11 insertions(+), 66 deletions(-)
diff --git a/dlls/winemac.drv/opengl.c b/dlls/winemac.drv/opengl.c index dd3b3e4da59..57b2ed23dd9 100644 --- a/dlls/winemac.drv/opengl.c +++ b/dlls/winemac.drv/opengl.c @@ -93,9 +93,6 @@ static struct gl_drawable *impl_from_opengl_drawable(struct opengl_drawable *bas static struct list context_list = LIST_INIT(context_list); static pthread_mutex_t context_mutex = PTHREAD_MUTEX_INITIALIZER;
-static CFMutableDictionaryRef dc_pbuffers; -static pthread_mutex_t dc_pbuffers_mutex = PTHREAD_MUTEX_INITIALIZER; - static void *opengl_handle; static const struct opengl_funcs *funcs; static const struct opengl_driver_funcs macdrv_driver_funcs; @@ -2370,10 +2367,6 @@ static BOOL macdrv_pbuffer_create(HDC hdc, int format, BOOL largest, GLenum text return FALSE; }
- pthread_mutex_lock(&dc_pbuffers_mutex); - CFDictionarySetValue(dc_pbuffers, hdc, gl->pbuffer); - pthread_mutex_unlock(&dc_pbuffers_mutex); - *drawable = &gl->base; TRACE(" -> %p\n", gl); return TRUE; @@ -2385,10 +2378,6 @@ static void macdrv_pbuffer_destroy(struct opengl_drawable *base)
TRACE("drawable %s\n", debugstr_opengl_drawable(base));
- pthread_mutex_lock(&dc_pbuffers_mutex); - CFDictionaryRemoveValue(dc_pbuffers, gl->pbuffer); - pthread_mutex_unlock(&dc_pbuffers_mutex); - CGLReleasePBuffer(gl->pbuffer); }
@@ -2439,22 +2428,9 @@ static BOOL macdrv_make_current(struct opengl_drawable *draw_base, struct opengl } else { - CGLPBufferObj pbuffer; - - pthread_mutex_lock(&dc_pbuffers_mutex); - pbuffer = (CGLPBufferObj)CFDictionaryGetValue(dc_pbuffers, draw->base.hdc); - if (!pbuffer) - { - WARN("no window or pbuffer for DC\n"); - pthread_mutex_unlock(&dc_pbuffers_mutex); - RtlSetLastWin32Error(ERROR_INVALID_HANDLE); - return FALSE; - } - context->draw_hwnd = NULL; context->draw_view = NULL; - context->draw_pbuffer = pbuffer; - pthread_mutex_unlock(&dc_pbuffers_mutex); + context->draw_pbuffer = draw->pbuffer; }
context->read_view = NULL; @@ -2476,9 +2452,7 @@ static BOOL macdrv_make_current(struct opengl_drawable *draw_base, struct opengl } else { - pthread_mutex_lock(&dc_pbuffers_mutex); - context->read_pbuffer = (CGLPBufferObj)CFDictionaryGetValue(dc_pbuffers, read->base.hdc); - pthread_mutex_unlock(&dc_pbuffers_mutex); + context->read_pbuffer = read->pbuffer; } }
@@ -2790,13 +2764,6 @@ UINT macdrv_OpenGLInit(UINT version, const struct opengl_funcs *opengl_funcs, co } funcs = opengl_funcs;
- dc_pbuffers = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); - if (!dc_pbuffers) - { - WARN("CFDictionaryCreateMutable failed\n"); - return STATUS_NOT_SUPPORTED; - } - opengl_handle = dlopen("/System/Library/Frameworks/OpenGL.framework/OpenGL", RTLD_LAZY|RTLD_LOCAL|RTLD_NOLOAD); if (!opengl_handle) { @@ -2951,49 +2918,27 @@ static void *macdrv_get_proc_address(const char *name) static BOOL macdrv_surface_swap(struct opengl_drawable *base) { struct macdrv_context *context = NtCurrentTeb()->glReserved2; + struct macdrv_win_data *data; HWND hwnd = base->hwnd; - HDC hdc = base->hdc; BOOL match = FALSE;
- TRACE("hdc %p context %p/%p/%p\n", hdc, context, (context ? context->context : NULL), + TRACE("%s context %p/%p/%p\n", debugstr_opengl_drawable(base), context, (context ? context->context : NULL), (context ? context->cglcontext : NULL));
- if (hwnd) + if (!(data = get_win_data(hwnd))) { - struct macdrv_win_data *data; - - if (!(data = get_win_data(hwnd))) - { - RtlSetLastWin32Error(ERROR_INVALID_HANDLE); - return FALSE; - } - - if (context && context->draw_view == data->client_cocoa_view) - match = TRUE; - - release_win_data(data); + RtlSetLastWin32Error(ERROR_INVALID_HANDLE); + return FALSE; } - else - { - CGLPBufferObj pbuffer;
- pthread_mutex_lock(&dc_pbuffers_mutex); - pbuffer = (CGLPBufferObj)CFDictionaryGetValue(dc_pbuffers, hdc); - pthread_mutex_unlock(&dc_pbuffers_mutex); + if (context && context->draw_view == data->client_cocoa_view) + match = TRUE;
- if (!pbuffer) - { - RtlSetLastWin32Error(ERROR_INVALID_HANDLE); - return FALSE; - } - - if (context && context->draw_pbuffer == pbuffer) - match = TRUE; - } + release_win_data(data);
if (!match) { - FIXME("current context %p doesn't match hdc %p; can't swap\n", context, hdc); + FIXME("current context %p doesn't match; can't swap\n", context); return FALSE; }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/opengl.c | 14 ++++++++++---- dlls/winemac.drv/opengl.c | 16 ---------------- dlls/winex11.drv/opengl.c | 16 ---------------- 3 files changed, 10 insertions(+), 36 deletions(-)
diff --git a/dlls/win32u/opengl.c b/dlls/win32u/opengl.c index 65318895a65..f89f16c2f48 100644 --- a/dlls/win32u/opengl.c +++ b/dlls/win32u/opengl.c @@ -246,9 +246,12 @@ void opengl_drawable_release( struct opengl_drawable *drawable ) const struct opengl_funcs *funcs = &display_funcs; const struct egl_platform *egl = &display_egl;
- pthread_mutex_lock( &drawables_lock ); - opengl_drawable_detach( drawable ); - pthread_mutex_unlock( &drawables_lock ); + if (drawable->hwnd) + { + pthread_mutex_lock( &drawables_lock ); + opengl_drawable_detach( drawable ); + pthread_mutex_unlock( &drawables_lock ); + }
drawable->funcs->destroy( drawable ); if (drawable->surface) funcs->p_eglDestroySurface( egl->display, drawable->surface ); @@ -258,6 +261,8 @@ void opengl_drawable_release( struct opengl_drawable *drawable )
static void opengl_drawable_flush( struct opengl_drawable *drawable, int interval, UINT flags ) { + if (!drawable->hwnd) return; + if (InterlockedCompareExchange( &drawable->updated, 0, 1 )) flags |= GL_FLUSH_UPDATED; if (interval != drawable->interval) { @@ -1658,7 +1663,8 @@ static BOOL win32u_wglSwapBuffers( HDC hdc )
if (!(draw = get_dc_opengl_drawable( draw_hdc, FALSE ))) return FALSE; opengl_drawable_flush( draw, interval, 0 ); - ret = draw->funcs->swap( draw ); + if (!draw->hwnd) ret = FALSE; /* pbuffer, nothing to do */ + else ret = draw->funcs->swap( draw ); opengl_drawable_release( draw );
return ret; diff --git a/dlls/winemac.drv/opengl.c b/dlls/winemac.drv/opengl.c index 57b2ed23dd9..4d9780c7eb3 100644 --- a/dlls/winemac.drv/opengl.c +++ b/dlls/winemac.drv/opengl.c @@ -2381,19 +2381,6 @@ static void macdrv_pbuffer_destroy(struct opengl_drawable *base) CGLReleasePBuffer(gl->pbuffer); }
-static void macdrv_pbuffer_detach(struct opengl_drawable *base) -{ -} - -static void macdrv_pbuffer_flush(struct opengl_drawable *base, UINT flags) -{ -} - -static BOOL macdrv_pbuffer_swap(struct opengl_drawable *base) -{ - return FALSE; -} - static BOOL macdrv_make_current(struct opengl_drawable *draw_base, struct opengl_drawable *read_base, void *private) { struct gl_drawable *draw = impl_from_opengl_drawable(draw_base), *read = impl_from_opengl_drawable(read_base); @@ -2973,7 +2960,4 @@ static const struct opengl_drawable_funcs macdrv_surface_funcs = static const struct opengl_drawable_funcs macdrv_pbuffer_funcs = { .destroy = macdrv_pbuffer_destroy, - .detach = macdrv_pbuffer_detach, - .flush = macdrv_pbuffer_flush, - .swap = macdrv_pbuffer_swap, }; diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index 547608aa79f..4d100088030 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -1428,19 +1428,6 @@ static void x11drv_pbuffer_destroy( struct opengl_drawable *base ) if (gl->drawable) pglXDestroyPbuffer( gdi_display, gl->drawable ); }
-static void x11drv_pbuffer_detach( struct opengl_drawable *base ) -{ -} - -static void x11drv_pbuffer_flush( struct opengl_drawable *base, UINT flags ) -{ -} - -static BOOL x11drv_pbuffer_swap( struct opengl_drawable *base ) -{ - return FALSE; -} - static BOOL x11drv_pbuffer_updated( HDC hdc, struct opengl_drawable *base, GLenum cube_face, GLint mipmap_level ) { return GL_TRUE; @@ -1669,9 +1656,6 @@ static const struct opengl_drawable_funcs x11drv_surface_funcs = static const struct opengl_drawable_funcs x11drv_pbuffer_funcs = { .destroy = x11drv_pbuffer_destroy, - .detach = x11drv_pbuffer_detach, - .flush = x11drv_pbuffer_flush, - .swap = x11drv_pbuffer_swap, };
static const struct opengl_drawable_funcs x11drv_egl_surface_funcs =
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/opengl.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-)
diff --git a/dlls/win32u/opengl.c b/dlls/win32u/opengl.c index f89f16c2f48..1c89b49b483 100644 --- a/dlls/win32u/opengl.c +++ b/dlls/win32u/opengl.c @@ -166,16 +166,9 @@ static void register_extension( char *list, size_t size, const char *name ) static pthread_mutex_t drawables_lock = PTHREAD_MUTEX_INITIALIZER; static struct list drawables = LIST_INIT( drawables );
-/* drawables_lock must be held */ -static void opengl_drawable_detach( struct opengl_drawable *drawable ) -{ - drawable->funcs->detach( drawable ); - list_remove( &drawable->entry ); - list_init( &drawable->entry ); -} - void detach_opengl_drawables( HWND hwnd ) { + struct list detached = LIST_INIT( detached ); struct opengl_drawable *drawable, *next;
pthread_mutex_lock( &drawables_lock ); @@ -183,10 +176,22 @@ void detach_opengl_drawables( HWND hwnd ) LIST_FOR_EACH_ENTRY_SAFE( drawable, next, &drawables, struct opengl_drawable, entry ) { if (drawable->hwnd != hwnd) continue; - opengl_drawable_detach( drawable ); + + list_remove( &drawable->entry ); + list_add_tail( &detached, &drawable->entry ); + opengl_drawable_add_ref( drawable ); + + drawable->funcs->detach( drawable ); + drawable->hwnd = NULL; }
pthread_mutex_unlock( &drawables_lock ); + + LIST_FOR_EACH_ENTRY_SAFE( drawable, next, &detached, struct opengl_drawable, entry ) + { + list_remove( &drawable->entry ); + opengl_drawable_release( drawable ); + } }
void update_opengl_drawables( HWND hwnd ) @@ -246,12 +251,13 @@ void opengl_drawable_release( struct opengl_drawable *drawable ) const struct opengl_funcs *funcs = &display_funcs; const struct egl_platform *egl = &display_egl;
+ pthread_mutex_lock( &drawables_lock ); if (drawable->hwnd) { - pthread_mutex_lock( &drawables_lock ); - opengl_drawable_detach( drawable ); - pthread_mutex_unlock( &drawables_lock ); + drawable->funcs->detach( drawable ); + list_remove( &drawable->entry ); } + pthread_mutex_unlock( &drawables_lock );
drawable->funcs->destroy( drawable ); if (drawable->surface) funcs->p_eglDestroySurface( egl->display, drawable->surface );
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/opengl.c | 94 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 6 deletions(-)
diff --git a/dlls/win32u/opengl.c b/dlls/win32u/opengl.c index 1c89b49b483..f5071657e23 100644 --- a/dlls/win32u/opengl.c +++ b/dlls/win32u/opengl.c @@ -281,6 +281,15 @@ static void opengl_drawable_flush( struct opengl_drawable *drawable, int interva
#ifdef SONAME_LIBEGL
+static const struct opengl_drawable_funcs egldrv_pbuffer_funcs; + +static inline EGLConfig egl_config_for_format( const struct egl_platform *egl, int format ) +{ + assert(format > 0 && format <= 2 * egl->config_count); + if (format <= egl->config_count) return egl->configs[format - 1]; + return egl->configs[format - egl->config_count - 1]; +} + static void *egldrv_get_proc_address( const char *name ) { return display_funcs.p_eglGetProcAddress( name ); @@ -293,7 +302,6 @@ static UINT egldrv_init_pixel_formats( UINT *onscreen_count ) const EGLint attribs[] = { EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; EGLConfig *configs; @@ -420,7 +428,9 @@ static BOOL describe_egl_config( EGLConfig config, struct wgl_pixel_format *fmt, } else fmt->pixel_type = -1;
- fmt->draw_to_pbuffer = TRUE; + if ((fmt->draw_to_pbuffer = !!(surface_type & EGL_PBUFFER_BIT))) + pfd->dwFlags |= PFD_DRAW_TO_BITMAP; + /* Use some arbitrary but reasonable limits (4096 is also Mesa's default) */ fmt->max_pbuffer_width = 4096; fmt->max_pbuffer_height = 4096; @@ -489,19 +499,81 @@ static BOOL egldrv_surface_create( HWND hwnd, HDC hdc, int format, struct opengl static BOOL egldrv_pbuffer_create( HDC hdc, int format, BOOL largest, GLenum texture_format, GLenum texture_target, GLint max_level, GLsizei *width, GLsizei *height, struct opengl_drawable **drawable ) { - FIXME( "stub!\n" ); - return FALSE; + const struct opengl_funcs *funcs = &display_funcs; + const struct egl_platform *egl = &display_egl; + EGLint attribs[13], *attrib = attribs; + struct opengl_drawable *gl; + + TRACE( "hdc %p, format %d, largest %u, texture_format %#x, texture_target %#x, max_level %#x, width %d, height %d, drawable %p\n", + hdc, format, largest, texture_format, texture_target, max_level, *width, *height, drawable ); + + *attrib++ = EGL_WIDTH; + *attrib++ = *width; + *attrib++ = EGL_HEIGHT; + *attrib++ = *height; + if (largest) + { + *attrib++ = EGL_LARGEST_PBUFFER; + *attrib++ = 1; + } + switch (texture_format) + { + case 0: break; + case GL_RGB: + *attrib++ = EGL_TEXTURE_FORMAT; + *attrib++ = EGL_TEXTURE_RGB; + break; + case GL_RGBA: + *attrib++ = EGL_TEXTURE_FORMAT; + *attrib++ = EGL_TEXTURE_RGBA; + break; + default: + FIXME( "Unsupported format %#x\n", texture_format ); + *attrib++ = EGL_TEXTURE_FORMAT; + *attrib++ = EGL_TEXTURE_RGBA; + break; + } + switch (texture_target) + { + case 0: break; + case GL_TEXTURE_2D: + *attrib++ = EGL_TEXTURE_TARGET; + *attrib++ = EGL_TEXTURE_2D; + break; + default: + FIXME( "Unsupported target %#x\n", texture_target ); + *attrib++ = EGL_TEXTURE_TARGET; + *attrib++ = EGL_TEXTURE_2D; + break; + } + if (max_level) + { + *attrib++ = EGL_MIPMAP_TEXTURE; + *attrib++ = GL_TRUE; + } + *attrib++ = EGL_NONE; + + if (!(gl = opengl_drawable_create( sizeof(*gl), &egldrv_pbuffer_funcs, format, 0, hdc ))) return FALSE; + if (!(gl->surface = funcs->p_eglCreatePbufferSurface( egl->display, egl_config_for_format( egl, gl->format ), attribs ))) + { + opengl_drawable_release( gl ); + return FALSE; + } + + funcs->p_eglQuerySurface( egl->display, gl->surface, EGL_WIDTH, width ); + funcs->p_eglQuerySurface( egl->display, gl->surface, EGL_HEIGHT, height ); + + *drawable = gl; + return TRUE; }
static BOOL egldrv_pbuffer_updated( HDC hdc, struct opengl_drawable *drawable, GLenum cube_face, GLint mipmap_level ) { - FIXME( "stub!\n" ); return GL_TRUE; }
static UINT egldrv_pbuffer_bind( HDC hdc, struct opengl_drawable *drawable, GLenum buffer ) { - FIXME( "stub!\n" ); return -1; /* use default implementation */ }
@@ -596,6 +668,16 @@ static BOOL egldrv_make_current( struct opengl_drawable *draw, struct opengl_dra return funcs->p_eglMakeCurrent( egl->display, context ? draw->surface : EGL_NO_SURFACE, context ? read->surface : EGL_NO_SURFACE, context ); }
+static void egldrv_pbuffer_destroy( struct opengl_drawable *drawable ) +{ + TRACE( "%s\n", debugstr_opengl_drawable( drawable ) ); +} + +static const struct opengl_drawable_funcs egldrv_pbuffer_funcs = +{ + .destroy = egldrv_pbuffer_destroy, +}; + static const struct opengl_driver_funcs egldrv_funcs = { .p_get_proc_address = egldrv_get_proc_address,
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winewayland.drv/opengl.c | 136 +++++++++++++++++----------------- 1 file changed, 70 insertions(+), 66 deletions(-)
diff --git a/dlls/winewayland.drv/opengl.c b/dlls/winewayland.drv/opengl.c index 9f1660a80a6..3becd7cbe60 100644 --- a/dlls/winewayland.drv/opengl.c +++ b/dlls/winewayland.drv/opengl.c @@ -51,7 +51,6 @@ struct wayland_gl_drawable struct opengl_drawable base; struct wayland_client_surface *client; struct wl_egl_window *wl_egl_window; - BOOL double_buffered; };
static struct wayland_gl_drawable *impl_from_opengl_drawable(struct opengl_drawable *base) @@ -86,68 +85,13 @@ static void wayland_drawable_update(struct opengl_drawable *base) TRACE("%s\n", debugstr_opengl_drawable(base)); }
-static inline BOOL is_onscreen_format(int format) -{ - return format > 0 && format <= egl->config_count; -} - -static inline EGLConfig egl_config_for_format(int format) +static EGLConfig egl_config_for_format(int format) { assert(format > 0 && format <= 2 * egl->config_count); if (format <= egl->config_count) return egl->configs[format - 1]; return egl->configs[format - egl->config_count - 1]; }
-static struct wayland_gl_drawable *wayland_gl_drawable_create(HWND hwnd, HDC hdc, int format, int width, int height) -{ - struct wayland_gl_drawable *gl; - EGLint attribs[4], *attrib = attribs; - - TRACE("hwnd=%p format=%d\n", hwnd, format); - - if (!egl->has_EGL_EXT_present_opaque) - WARN("Missing EGL_EXT_present_opaque extension\n"); - else - { - *attrib++ = EGL_PRESENT_OPAQUE_EXT; - *attrib++ = EGL_TRUE; - } - *attrib++ = EGL_NONE; - - if (!(gl = opengl_drawable_create(sizeof(*gl), &wayland_drawable_funcs, format, hwnd, hdc))) return NULL; - - /* Get the client surface for the HWND. If don't have a wayland surface - * (e.g., HWND_MESSAGE windows) just create a dummy surface to act as the - * target render surface. */ - if (!(gl->client = wayland_client_surface_create(hwnd))) goto err; - set_client_surface(hwnd, gl->client); - - gl->wl_egl_window = wl_egl_window_create(gl->client->wl_surface, width, height); - if (!gl->wl_egl_window) - { - ERR("Failed to create wl_egl_window\n"); - goto err; - } - - gl->base.surface = funcs->p_eglCreateWindowSurface(egl->display, egl_config_for_format(format), - gl->wl_egl_window, attribs); - if (!gl->base.surface) - { - ERR("Failed to create EGL surface\n"); - goto err; - } - - gl->double_buffered = is_onscreen_format(format); - - TRACE("Created drawable %s with egl_surface %p\n", debugstr_opengl_drawable(&gl->base), gl->base.surface); - - return gl; - -err: - opengl_drawable_release(&gl->base); - return NULL; -} - static void wayland_gl_drawable_sync_size(struct wayland_gl_drawable *gl) { int client_width, client_height; @@ -163,20 +107,44 @@ static void wayland_gl_drawable_sync_size(struct wayland_gl_drawable *gl)
static BOOL wayland_opengl_surface_create(HWND hwnd, HDC hdc, int format, struct opengl_drawable **drawable) { + EGLConfig config = egl_config_for_format(format); + EGLint attribs[4], *attrib = attribs; struct opengl_drawable *previous; struct wayland_gl_drawable *gl; RECT rect;
+ TRACE("hwnd=%p format=%d\n", hwnd, format); + if ((previous = *drawable) && previous->format == format) return TRUE;
NtUserGetClientRect(hwnd, &rect, NtUserGetDpiForWindow(hwnd)); if (rect.right == rect.left) rect.right = rect.left + 1; if (rect.bottom == rect.top) rect.bottom = rect.top + 1;
- if (!(gl = wayland_gl_drawable_create(hwnd, 0, format, rect.right - rect.left, rect.bottom - rect.top))) return FALSE; + if (!egl->has_EGL_EXT_present_opaque) + WARN("Missing EGL_EXT_present_opaque extension\n"); + else + { + *attrib++ = EGL_PRESENT_OPAQUE_EXT; + *attrib++ = EGL_TRUE; + } + *attrib++ = EGL_NONE; + + if (!(gl = opengl_drawable_create(sizeof(*gl), &wayland_drawable_funcs, format, hwnd, hdc))) return FALSE; + if (!(gl->client = wayland_client_surface_create(hwnd))) goto err; + if (!(gl->wl_egl_window = wl_egl_window_create(gl->client->wl_surface, rect.right, rect.bottom))) goto err; + if (!(gl->base.surface = funcs->p_eglCreateWindowSurface(egl->display, config, gl->wl_egl_window, attribs))) goto err; + set_client_surface(hwnd, gl->client); + + TRACE("Created drawable %s with egl_surface %p\n", debugstr_opengl_drawable(&gl->base), gl->base.surface); + if (previous) opengl_drawable_release( previous ); *drawable = &gl->base; return TRUE; + +err: + opengl_drawable_release(&gl->base); + return FALSE; }
static EGLenum wayland_init_egl_platform(const struct egl_platform *platform, EGLNativeDisplayType *platform_display) @@ -206,26 +174,62 @@ static BOOL wayland_drawable_swap(struct opengl_drawable *base)
ensure_window_surface_contents(toplevel); set_client_surface(hwnd, gl->client); - - /* Although all the EGL surfaces we create are double-buffered, we want to - * use some as single-buffered, so avoid swapping those. */ - if (gl->double_buffered) funcs->p_eglSwapBuffers(egl->display, gl->base.surface); + funcs->p_eglSwapBuffers(egl->display, gl->base.surface);
return TRUE; }
+struct wayland_pbuffer +{ + struct opengl_drawable base; + struct wl_surface *surface; + struct wl_egl_window *window; +}; + +static struct wayland_pbuffer *pbuffer_from_opengl_drawable(struct opengl_drawable *base) +{ + return CONTAINING_RECORD(base, struct wayland_pbuffer, base); +} + +static void wayland_pbuffer_destroy(struct opengl_drawable *base) +{ + struct wayland_pbuffer *gl = pbuffer_from_opengl_drawable(base); + + TRACE("%s\n", debugstr_opengl_drawable(base)); + + if (gl->window) + wl_egl_window_destroy(gl->window); + if (gl->surface) + wl_surface_destroy(gl->surface); +} + +static const struct opengl_drawable_funcs wayland_pbuffer_funcs = +{ + .destroy = wayland_pbuffer_destroy, +}; + static BOOL wayland_pbuffer_create(HDC hdc, int format, BOOL largest, GLenum texture_format, GLenum texture_target, GLint max_level, GLsizei *width, GLsizei *height, struct opengl_drawable **surface) { - struct wayland_gl_drawable *drawable; + EGLConfig config = egl_config_for_format(format); + struct wayland_pbuffer *gl;
TRACE("hdc %p, format %d, largest %u, texture_format %#x, texture_target %#x, max_level %#x, width %d, height %d, private %p\n", hdc, format, largest, texture_format, texture_target, max_level, *width, *height, surface);
- /* Use an unmapped wayland surface as our offscreen "pbuffer" surface. */ - if (!(drawable = wayland_gl_drawable_create(0, hdc, format, *width, *height))) return FALSE; - *surface = &drawable->base; + if (!(gl = opengl_drawable_create(sizeof(*gl), &wayland_pbuffer_funcs, format, NULL, hdc))) return FALSE; + /* Wayland EGL doesn't support pixmap or pbuffer, create a dummy window surface to act as the target render surface. */ + if (!(gl->surface = wl_compositor_create_surface(process_wayland.wl_compositor))) goto err; + if (!(gl->window = wl_egl_window_create(gl->surface, *width, *height))) goto err; + if (!(gl->base.surface = funcs->p_eglCreateWindowSurface(egl->display, config, gl->window, NULL))) goto err; + + TRACE("Created pbuffer %s with egl_surface %p\n", debugstr_opengl_drawable(&gl->base), gl->base.surface); + *surface = &gl->base; return TRUE; + +err: + opengl_drawable_release(&gl->base); + return FALSE; }
static BOOL wayland_pbuffer_updated(HDC hdc, struct opengl_drawable *base, GLenum cube_face, GLint mipmap_level)
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/opengl.c | 5 ++--- dlls/wineandroid.drv/opengl.c | 3 +-- dlls/winemac.drv/opengl.c | 4 ++-- dlls/winewayland.drv/opengl.c | 4 ++-- dlls/winex11.drv/opengl.c | 17 +++++++++-------- include/wine/opengl_driver.h | 5 ++--- 6 files changed, 18 insertions(+), 20 deletions(-)
diff --git a/dlls/win32u/opengl.c b/dlls/win32u/opengl.c index f5071657e23..7b1afb7571a 100644 --- a/dlls/win32u/opengl.c +++ b/dlls/win32u/opengl.c @@ -210,7 +210,7 @@ void update_opengl_drawables( HWND hwnd ) pthread_mutex_unlock( &drawables_lock ); }
-void *opengl_drawable_create( UINT size, const struct opengl_drawable_funcs *funcs, int format, HWND hwnd, HDC hdc ) +void *opengl_drawable_create( UINT size, const struct opengl_drawable_funcs *funcs, int format, HWND hwnd ) { struct opengl_drawable *drawable;
@@ -221,7 +221,6 @@ void *opengl_drawable_create( UINT size, const struct opengl_drawable_funcs *fun drawable->format = format; drawable->interval = INT_MIN; drawable->hwnd = hwnd; - drawable->hdc = hdc;
if (!hwnd) list_init( &drawable->entry ); /* pbuffer, keep it unlinked */ else @@ -553,7 +552,7 @@ static BOOL egldrv_pbuffer_create( HDC hdc, int format, BOOL largest, GLenum tex } *attrib++ = EGL_NONE;
- if (!(gl = opengl_drawable_create( sizeof(*gl), &egldrv_pbuffer_funcs, format, 0, hdc ))) return FALSE; + if (!(gl = opengl_drawable_create( sizeof(*gl), &egldrv_pbuffer_funcs, format, 0 ))) return FALSE; if (!(gl->surface = funcs->p_eglCreatePbufferSurface( egl->display, egl_config_for_format( egl, gl->format ), attribs ))) { opengl_drawable_release( gl ); diff --git a/dlls/wineandroid.drv/opengl.c b/dlls/wineandroid.drv/opengl.c index ba72dbd4979..fde0e24c02d 100644 --- a/dlls/wineandroid.drv/opengl.c +++ b/dlls/wineandroid.drv/opengl.c @@ -75,7 +75,7 @@ static struct gl_drawable *create_gl_drawable( HWND hwnd, HDC hdc, int format, A static const int attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; struct gl_drawable *gl;
- if (!(gl = opengl_drawable_create( sizeof(*gl), &android_drawable_funcs, format, hwnd, hdc ))) return NULL; + if (!(gl = opengl_drawable_create( sizeof(*gl), &android_drawable_funcs, format, hwnd ))) return NULL;
if (!window) gl->window = create_ioctl_window( hwnd, TRUE, 1.0f ); else gl->window = grab_ioctl_window( window ); @@ -134,7 +134,6 @@ static BOOL android_surface_create( HWND hwnd, HDC hdc, int format, struct openg funcs->p_eglGetConfigAttrib( egl->display, egl_config_for_format(format), EGL_NATIVE_VISUAL_ID, &pf ); gl->window->perform( gl->window, NATIVE_WINDOW_SET_BUFFERS_FORMAT, pf ); gl->base.hwnd = hwnd; - gl->base.hdc = hdc; gl->base.format = format;
TRACE( "Updated drawable %s\n", debugstr_opengl_drawable( *drawable ) ); diff --git a/dlls/winemac.drv/opengl.c b/dlls/winemac.drv/opengl.c index 4d9780c7eb3..f48737c1b43 100644 --- a/dlls/winemac.drv/opengl.c +++ b/dlls/winemac.drv/opengl.c @@ -1484,7 +1484,7 @@ static BOOL macdrv_surface_create(HWND hwnd, HDC hdc, int format, struct opengl_ data->pixel_format = format; release_win_data(data);
- if (!(gl = opengl_drawable_create(sizeof(*gl), &macdrv_surface_funcs, format, hwnd, hdc))) return FALSE; + if (!(gl = opengl_drawable_create(sizeof(*gl), &macdrv_surface_funcs, format, hwnd))) return FALSE; *drawable = &gl->base;
return TRUE; @@ -2357,7 +2357,7 @@ static BOOL macdrv_pbuffer_create(HDC hdc, int format, BOOL largest, GLenum text texture_format = GL_RGB; }
- if (!(gl = opengl_drawable_create(sizeof(*gl), &macdrv_pbuffer_funcs, format, 0, hdc))) return FALSE; + if (!(gl = opengl_drawable_create(sizeof(*gl), &macdrv_pbuffer_funcs, format, 0))) return FALSE;
err = CGLCreatePBuffer(*width, *height, texture_target, texture_format, max_level, &gl->pbuffer); if (err != kCGLNoError) diff --git a/dlls/winewayland.drv/opengl.c b/dlls/winewayland.drv/opengl.c index 3becd7cbe60..49060c45784 100644 --- a/dlls/winewayland.drv/opengl.c +++ b/dlls/winewayland.drv/opengl.c @@ -130,7 +130,7 @@ static BOOL wayland_opengl_surface_create(HWND hwnd, HDC hdc, int format, struct } *attrib++ = EGL_NONE;
- if (!(gl = opengl_drawable_create(sizeof(*gl), &wayland_drawable_funcs, format, hwnd, hdc))) return FALSE; + if (!(gl = opengl_drawable_create(sizeof(*gl), &wayland_drawable_funcs, format, hwnd))) return FALSE; if (!(gl->client = wayland_client_surface_create(hwnd))) goto err; if (!(gl->wl_egl_window = wl_egl_window_create(gl->client->wl_surface, rect.right, rect.bottom))) goto err; if (!(gl->base.surface = funcs->p_eglCreateWindowSurface(egl->display, config, gl->wl_egl_window, attribs))) goto err; @@ -217,7 +217,7 @@ static BOOL wayland_pbuffer_create(HDC hdc, int format, BOOL largest, GLenum tex TRACE("hdc %p, format %d, largest %u, texture_format %#x, texture_target %#x, max_level %#x, width %d, height %d, private %p\n", hdc, format, largest, texture_format, texture_target, max_level, *width, *height, surface);
- if (!(gl = opengl_drawable_create(sizeof(*gl), &wayland_pbuffer_funcs, format, NULL, hdc))) return FALSE; + if (!(gl = opengl_drawable_create(sizeof(*gl), &wayland_pbuffer_funcs, format, NULL))) return FALSE; /* Wayland EGL doesn't support pixmap or pbuffer, create a dummy window surface to act as the target render surface. */ if (!(gl->surface = wl_compositor_create_surface(process_wayland.wl_compositor))) goto err; if (!(gl->window = wl_egl_window_create(gl->surface, *width, *height))) goto err; diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index 4d100088030..8581ace0e6d 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -203,6 +203,7 @@ struct gl_drawable Colormap colormap; /* colormap for the client window */ Pixmap pixmap; /* base pixmap if drawable is a GLXPixmap */ BOOL offscreen; + HDC hdc; HDC hdc_src; HDC hdc_dst; }; @@ -493,16 +494,16 @@ static BOOL x11drv_egl_surface_create( HWND hwnd, HDC hdc, int format, struct op if ((previous = *drawable) && previous->format == format) return TRUE; NtUserGetClientRect( hwnd, &rect, NtUserGetDpiForWindow( hwnd ) );
- if (!(gl = opengl_drawable_create( sizeof(*gl), &x11drv_egl_surface_funcs, format, hwnd, hdc ))) return FALSE; + if (!(gl = opengl_drawable_create( sizeof(*gl), &x11drv_egl_surface_funcs, format, hwnd ))) return FALSE; gl->rect = rect; + gl->hdc = hdc;
gl->window = create_client_window( hwnd, &default_visual, default_colormap ); gl->base.surface = funcs->p_eglCreateWindowSurface( egl->display, egl_config_for_format(format), (void *)gl->window, NULL ); if (!gl->base.surface) { - destroy_client_window( hwnd, gl->window ); - free( gl ); + opengl_drawable_release( &gl->base ); return FALSE; }
@@ -934,8 +935,9 @@ static BOOL x11drv_surface_create( HWND hwnd, HDC hdc, int format, struct opengl if ((previous = *drawable) && previous->format == format) return TRUE; NtUserGetClientRect( hwnd, &rect, NtUserGetDpiForWindow( hwnd ) );
- if (!(gl = opengl_drawable_create( sizeof(*gl), &x11drv_surface_funcs, format, hwnd, hdc ))) return FALSE; + if (!(gl = opengl_drawable_create( sizeof(*gl), &x11drv_surface_funcs, format, hwnd ))) return FALSE; gl->rect = rect; + gl->hdc = hdc;
gl->colormap = XCreateColormap( gdi_display, get_dummy_parent(), fmt->visual->visual, (fmt->visual->class == PseudoColor || fmt->visual->class == GrayScale || @@ -1261,15 +1263,14 @@ static void present_gl_drawable( struct gl_drawable *gl, BOOL flush, BOOL gl_fin { HWND hwnd = gl->base.hwnd, toplevel = NtUserGetAncestor( hwnd, GA_ROOT ); struct x11drv_win_data *data; - HDC hdc = gl->base.hdc; Drawable window; RECT rect_dst, rect; HRGN region;
if (!gl->offscreen) return;
- window = get_dc_drawable( hdc, &rect ); - region = get_dc_monitor_region( hwnd, hdc ); + window = get_dc_drawable( gl->hdc, &rect ); + region = get_dc_monitor_region( hwnd, gl->hdc );
if (gl_finish) funcs->p_glFinish(); if (flush) XFlush( gdi_display ); @@ -1401,7 +1402,7 @@ static BOOL x11drv_pbuffer_create( HDC hdc, int format, BOOL largest, GLenum tex } glx_attribs[count++] = 0;
- if (!(gl = opengl_drawable_create( sizeof(*gl), &x11drv_pbuffer_funcs, format, 0, hdc ))) return FALSE; + if (!(gl = opengl_drawable_create( sizeof(*gl), &x11drv_pbuffer_funcs, format, 0 ))) return FALSE;
gl->drawable = pglXCreatePbuffer( gdi_display, fmt->fbconfig, glx_attribs ); TRACE( "new Pbuffer drawable as %p (%lx)\n", gl, gl->drawable ); diff --git a/include/wine/opengl_driver.h b/include/wine/opengl_driver.h index 188c7b81249..604bd6d6757 100644 --- a/include/wine/opengl_driver.h +++ b/include/wine/opengl_driver.h @@ -154,7 +154,6 @@ struct opengl_drawable int format; /* pixel format of the drawable */ int interval; /* last set surface swap interval */ HWND hwnd; /* window the drawable was created for */ - HDC hdc; /* DC the drawable was created for */ struct list entry; /* entry in win32u managed list */ LONG updated; /* has been moved / resized / reparented */ EGLSurface surface; /* surface for EGL based drivers */ @@ -163,10 +162,10 @@ struct opengl_drawable static inline const char *debugstr_opengl_drawable( struct opengl_drawable *drawable ) { if (!drawable) return "(null)"; - return wine_dbg_sprintf( "%p (format %u, hwnd %p, hdc %p)", drawable, drawable->format, drawable->hwnd, drawable->hdc ); + return wine_dbg_sprintf( "%p (format %u, hwnd %p)", drawable, drawable->format, drawable->hwnd ); }
-W32KAPI void *opengl_drawable_create( UINT size, const struct opengl_drawable_funcs *funcs, int format, HWND hwnd, HDC hdc ); +W32KAPI void *opengl_drawable_create( UINT size, const struct opengl_drawable_funcs *funcs, int format, HWND hwnd ); W32KAPI void opengl_drawable_add_ref( struct opengl_drawable *drawable ); W32KAPI void opengl_drawable_release( struct opengl_drawable *drawable );
v4: Remove now unused `is_onscreen_format`.
On Thu Jul 3 14:12:20 2025 +0000, Rémi Bernon wrote:
Hmm... I see, quite unfortunate that wayland GL has neither PBuffer nor Pixmaps... *sigh*.
FWIW, I have filed https://gitlab.freedesktop.org/mesa/mesa/-/issues/13454. It seems the time may be ripe to add Wayland EGL pbuffer support (although I am not sure how soon I will be able to get to it myself), so hopefully we will be able to remove this point of friction going forward. Let's see what upstream thinks.
On Thu Jul 3 14:12:20 2025 +0000, Rémi Bernon wrote:
v3: Keep winewayland pbuffer backed by wl_surface, split them from window drawables so they don't get in the way of later refactoring. @afrantzis all they need is a wl_surface and an wl_egl_window right? Could you confirm whether https://gitlab.winehq.org/wine/wine/-/merge_requests/8498/diffs?commit_id=50... would work?
Yes this should be enough. Note that we still need a way to expose "fake" pbuffer capable formats.
On Thu Jul 3 14:12:20 2025 +0000, Alexandros Frantzis wrote:
FWIW, I have filed https://gitlab.freedesktop.org/mesa/mesa/-/issues/13454. It seems the time may be ripe to add Wayland EGL pbuffer support (although I am not sure how soon I will be able to get to it myself), so hopefully we will be able to remove this point of friction going forward. Let's see what upstream thinks.
Thanks! I'm working on getting a generic swapchain implementation based on FBOs and exportable images anyway, so eventually we might be able to use it to implement pbuffers without requiring platform support.
Here I mostly just wanted to get pbuffers out of the way so I could make more changes the OpenGL window surface interface and make it converge with Vulkan surfaces, and I was mildly annoyed to find another lacking mechanism I assumed Wayland had already ;), but no big deal it should be fine like this.