I'm not completely sure about the mechanism, but I think it's simple enough to consider having that upstream now. This shows at least how we can leverage win32u surface changes to decide to switch surfaces on/off-screen and fallback to manual blitting.
Having the surfaces on-screen makes sure they are presented as efficiently as possible, having them off-screen we use GDI blit to indirectly call XCopyArea and this will be suboptimal, probably with visible tearing, but hopefully not too common.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/vulkan.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-)
diff --git a/dlls/win32u/vulkan.c b/dlls/win32u/vulkan.c index 59b9364065e..241df9a462e 100644 --- a/dlls/win32u/vulkan.c +++ b/dlls/win32u/vulkan.c @@ -45,6 +45,10 @@ static void *vulkan_handle; static const struct vulkan_driver_funcs *driver_funcs; static struct vulkan_funcs vulkan_funcs;
+/* list of surfaces attached to other processes / desktop windows */ +static struct list offscreen_surfaces = LIST_INIT(offscreen_surfaces); +static pthread_mutex_t vulkan_mutex = PTHREAD_MUTEX_INITIALIZER; + static void (*p_vkDestroySurfaceKHR)(VkInstance, VkSurfaceKHR, const VkAllocationCallbacks *); static VkResult (*p_vkQueuePresentKHR)(VkQueue, const VkPresentInfoKHR *); static void *(*p_vkGetDeviceProcAddr)(VkDevice, const char *); @@ -71,6 +75,7 @@ static inline VkSurfaceKHR surface_to_handle( struct surface *surface ) static VkResult win32u_vkCreateWin32SurfaceKHR( VkInstance instance, const VkWin32SurfaceCreateInfoKHR *info, const VkAllocationCallbacks *allocator, VkSurfaceKHR *handle ) { + HWND toplevel = NtUserGetAncestor( info->hwnd, GA_ROOT ); struct surface *surface; VkResult res; WND *win; @@ -85,8 +90,12 @@ static VkResult win32u_vkCreateWin32SurfaceKHR( VkInstance instance, const VkWin return res; }
- if (!(win = get_win_ptr( info->hwnd )) || win == WND_DESKTOP || win == WND_OTHER_PROCESS) - list_init( &surface->entry ); + if (!(win = get_win_ptr( toplevel )) || win == WND_DESKTOP || win == WND_OTHER_PROCESS) + { + pthread_mutex_lock( &vulkan_mutex ); + list_add_tail( &offscreen_surfaces, &surface->entry ); + pthread_mutex_unlock( &vulkan_mutex ); + } else { list_add_tail( &win->vulkan_surfaces, &surface->entry ); @@ -105,7 +114,10 @@ static void win32u_vkDestroySurfaceKHR( VkInstance instance, VkSurfaceKHR handle TRACE( "instance %p, handle 0x%s, allocator %p\n", instance, wine_dbgstr_longlong(handle), allocator ); if (allocator) FIXME( "Support for allocation callbacks not implemented yet\n" );
+ pthread_mutex_lock( &vulkan_mutex ); list_remove( &surface->entry ); + pthread_mutex_unlock( &vulkan_mutex ); + p_vkDestroySurfaceKHR( instance, surface->host_surface, NULL /* allocator */ ); driver_funcs->p_vulkan_surface_destroy( surface->hwnd, surface->driver_private ); free( surface ); @@ -258,14 +270,14 @@ static void vulkan_init(void)
void vulkan_detach_surfaces( struct list *surfaces ) { - struct surface *surface, *next; + struct surface *surface;
- LIST_FOR_EACH_ENTRY_SAFE( surface, next, surfaces, struct surface, entry ) - { + LIST_FOR_EACH_ENTRY( surface, surfaces, struct surface, entry ) driver_funcs->p_vulkan_surface_detach( surface->hwnd, surface->driver_private ); - list_remove( &surface->entry ); - list_init( &surface->entry ); - } + + pthread_mutex_lock( &vulkan_mutex ); + list_move_tail( &offscreen_surfaces, surfaces ); + pthread_mutex_unlock( &vulkan_mutex ); }
/***********************************************************************
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/ntuser_private.h | 1 + dlls/win32u/vulkan.c | 62 ++++++++++++++++++++++++++++++++++++ dlls/win32u/window.c | 1 + 3 files changed, 64 insertions(+)
diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 11b1a3ff8a1..04396abca72 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -257,6 +257,7 @@ extern LRESULT system_tray_call( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpar
/* vulkan.c */ extern void vulkan_detach_surfaces( struct list *surfaces ); +extern void vulkan_set_parent( HWND hwnd, HWND new_parent, HWND old_parent );
/* window.c */ HANDLE alloc_user_handle( struct user_object *ptr, unsigned int type ); diff --git a/dlls/win32u/vulkan.c b/dlls/win32u/vulkan.c index 241df9a462e..bf5a735bdd7 100644 --- a/dlls/win32u/vulkan.c +++ b/dlls/win32u/vulkan.c @@ -280,6 +280,68 @@ void vulkan_detach_surfaces( struct list *surfaces ) pthread_mutex_unlock( &vulkan_mutex ); }
+static void append_window_surfaces( HWND toplevel, struct list *surfaces ) +{ + WND *win; + + if (!(win = get_win_ptr( toplevel )) || win == WND_DESKTOP || win == WND_OTHER_PROCESS) + { + pthread_mutex_lock( &vulkan_mutex ); + list_move_tail( &offscreen_surfaces, surfaces ); + pthread_mutex_lock( &vulkan_mutex ); + } + else + { + list_move_tail( &win->vulkan_surfaces, surfaces ); + release_win_ptr( win ); + } +} + +static void enum_window_surfaces( HWND toplevel, HWND hwnd, struct list *surfaces ) +{ + struct list tmp_surfaces = LIST_INIT(tmp_surfaces); + struct surface *surface, *next; + WND *win; + + if (!(win = get_win_ptr( toplevel )) || win == WND_DESKTOP || win == WND_OTHER_PROCESS) + { + pthread_mutex_lock( &vulkan_mutex ); + list_move_tail( &tmp_surfaces, &offscreen_surfaces ); + pthread_mutex_lock( &vulkan_mutex ); + } + else + { + list_move_tail( &tmp_surfaces, &win->vulkan_surfaces ); + release_win_ptr( win ); + } + + LIST_FOR_EACH_ENTRY_SAFE( surface, next, &tmp_surfaces, struct surface, entry ) + { + if (surface->hwnd != hwnd && !NtUserIsChild( hwnd, surface->hwnd )) continue; + list_remove( &surface->entry ); + list_add_tail( surfaces, &surface->entry ); + } + + append_window_surfaces( toplevel, &tmp_surfaces ); +} + +void vulkan_set_parent( HWND hwnd, HWND new_parent, HWND old_parent ) +{ + struct list surfaces = LIST_INIT(surfaces); + HWND new_toplevel, old_toplevel; + + TRACE( "hwnd %p new_parent %p old_parent %p\n", hwnd, new_parent, old_parent ); + + if (new_parent == NtUserGetDesktopWindow()) new_toplevel = hwnd; + else new_toplevel = NtUserGetAncestor( new_parent, GA_ROOT ); + if (old_parent == NtUserGetDesktopWindow()) old_toplevel = hwnd; + else old_toplevel = NtUserGetAncestor( old_parent, GA_ROOT ); + if (old_toplevel == new_toplevel) return; + + enum_window_surfaces( old_toplevel, hwnd, &surfaces ); + append_window_surfaces( new_toplevel, &surfaces ); +} + /*********************************************************************** * __wine_get_vulkan_driver (win32u.so) */ diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 13dd1c8d80a..f630f107b00 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -463,6 +463,7 @@ HWND WINAPI NtUserSetParent( HWND hwnd, HWND parent ) context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( hwnd ));
user_driver->pSetParent( full_handle, parent, old_parent ); + vulkan_set_parent( full_handle, parent, old_parent );
winpos.hwnd = hwnd; winpos.hwndInsertAfter = HWND_TOP;
From: Rémi Bernon rbernon@codeweavers.com
And attach them back when they are on-screen again. --- dlls/win32u/vulkan.c | 15 +++++++++++++++ dlls/winemac.drv/vulkan.c | 5 +++++ dlls/winewayland.drv/vulkan.c | 5 +++++ dlls/winex11.drv/vulkan.c | 15 +++++++++++++++ dlls/winex11.drv/window.c | 2 +- dlls/winex11.drv/x11drv.h | 1 + include/wine/vulkan_driver.h | 3 ++- 7 files changed, 44 insertions(+), 2 deletions(-)
diff --git a/dlls/win32u/vulkan.c b/dlls/win32u/vulkan.c index bf5a735bdd7..e57aa440c81 100644 --- a/dlls/win32u/vulkan.c +++ b/dlls/win32u/vulkan.c @@ -92,6 +92,7 @@ static VkResult win32u_vkCreateWin32SurfaceKHR( VkInstance instance, const VkWin
if (!(win = get_win_ptr( toplevel )) || win == WND_DESKTOP || win == WND_OTHER_PROCESS) { + driver_funcs->p_vulkan_surface_detach( surface->hwnd, surface->driver_private ); pthread_mutex_lock( &vulkan_mutex ); list_add_tail( &offscreen_surfaces, &surface->entry ); pthread_mutex_unlock( &vulkan_mutex ); @@ -197,6 +198,10 @@ static void nulldrv_vulkan_surface_destroy( HWND hwnd, void *private ) { }
+static void nulldrv_vulkan_surface_attach( HWND hwnd, void *private ) +{ +} + static void nulldrv_vulkan_surface_detach( HWND hwnd, void *private ) { } @@ -219,6 +224,7 @@ static const struct vulkan_driver_funcs nulldrv_funcs = { .p_vulkan_surface_create = nulldrv_vulkan_surface_create, .p_vulkan_surface_destroy = nulldrv_vulkan_surface_destroy, + .p_vulkan_surface_attach = nulldrv_vulkan_surface_attach, .p_vulkan_surface_detach = nulldrv_vulkan_surface_detach, .p_vulkan_surface_presented = nulldrv_vulkan_surface_presented, .p_vkGetPhysicalDeviceWin32PresentationSupportKHR = nulldrv_vkGetPhysicalDeviceWin32PresentationSupportKHR, @@ -282,6 +288,7 @@ void vulkan_detach_surfaces( struct list *surfaces )
static void append_window_surfaces( HWND toplevel, struct list *surfaces ) { + struct surface *surface; WND *win;
if (!(win = get_win_ptr( toplevel )) || win == WND_DESKTOP || win == WND_OTHER_PROCESS) @@ -294,6 +301,9 @@ static void append_window_surfaces( HWND toplevel, struct list *surfaces ) { list_move_tail( &win->vulkan_surfaces, surfaces ); release_win_ptr( win ); + + LIST_FOR_EACH_ENTRY( surface, surfaces, struct surface, entry ) + driver_funcs->p_vulkan_surface_attach( surface->hwnd, surface->driver_private ); } }
@@ -329,6 +339,7 @@ void vulkan_set_parent( HWND hwnd, HWND new_parent, HWND old_parent ) { struct list surfaces = LIST_INIT(surfaces); HWND new_toplevel, old_toplevel; + struct surface *surface;
TRACE( "hwnd %p new_parent %p old_parent %p\n", hwnd, new_parent, old_parent );
@@ -339,6 +350,10 @@ void vulkan_set_parent( HWND hwnd, HWND new_parent, HWND old_parent ) if (old_toplevel == new_toplevel) return;
enum_window_surfaces( old_toplevel, hwnd, &surfaces ); + + LIST_FOR_EACH_ENTRY( surface, &surfaces, struct surface, entry ) + driver_funcs->p_vulkan_surface_detach( surface->hwnd, surface->driver_private ); + append_window_surfaces( new_toplevel, &surfaces ); }
diff --git a/dlls/winemac.drv/vulkan.c b/dlls/winemac.drv/vulkan.c index d5a3df579d0..9b033a84d57 100644 --- a/dlls/winemac.drv/vulkan.c +++ b/dlls/winemac.drv/vulkan.c @@ -178,6 +178,10 @@ static void macdrv_vulkan_surface_destroy(HWND hwnd, void *private) wine_vk_surface_destroy(mac_surface); }
+static void macdrv_vulkan_surface_attach(HWND hwnd, void *private) +{ +} + static void macdrv_vulkan_surface_detach(HWND hwnd, void *private) { } @@ -203,6 +207,7 @@ static const struct vulkan_driver_funcs macdrv_vulkan_driver_funcs = { .p_vulkan_surface_create = macdrv_vulkan_surface_create, .p_vulkan_surface_destroy = macdrv_vulkan_surface_destroy, + .p_vulkan_surface_attach = macdrv_vulkan_surface_attach, .p_vulkan_surface_detach = macdrv_vulkan_surface_detach, .p_vulkan_surface_presented = macdrv_vulkan_surface_presented,
diff --git a/dlls/winewayland.drv/vulkan.c b/dlls/winewayland.drv/vulkan.c index 16084175013..cd63981bf0d 100644 --- a/dlls/winewayland.drv/vulkan.c +++ b/dlls/winewayland.drv/vulkan.c @@ -132,6 +132,10 @@ static void wayland_vulkan_surface_destroy(HWND hwnd, void *private) wine_vk_surface_destroy(client); }
+static void wayland_vulkan_surface_attach(HWND hwnd, void *private) +{ +} + static void wayland_vulkan_surface_detach(HWND hwnd, void *private) { } @@ -175,6 +179,7 @@ static const struct vulkan_driver_funcs wayland_vulkan_driver_funcs = { .p_vulkan_surface_create = wayland_vulkan_surface_create, .p_vulkan_surface_destroy = wayland_vulkan_surface_destroy, + .p_vulkan_surface_attach = wayland_vulkan_surface_attach, .p_vulkan_surface_detach = wayland_vulkan_surface_detach, .p_vulkan_surface_presented = wayland_vulkan_surface_presented,
diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c index 85993bc517a..b979da1085e 100644 --- a/dlls/winex11.drv/vulkan.c +++ b/dlls/winex11.drv/vulkan.c @@ -109,6 +109,20 @@ static void X11DRV_vulkan_surface_destroy( HWND hwnd, void *private ) destroy_client_window( hwnd, client_window ); }
+static void X11DRV_vulkan_surface_attach( HWND hwnd, void *private ) +{ + Window client_window = (Window)private; + struct x11drv_win_data *data; + + TRACE( "%p %p\n", hwnd, private ); + + if ((data = get_win_data( hwnd ))) + { + attach_client_window( data, client_window ); + release_win_data( data ); + } +} + static void X11DRV_vulkan_surface_detach( HWND hwnd, void *private ) { Window client_window = (Window)private; @@ -145,6 +159,7 @@ static const struct vulkan_driver_funcs x11drv_vulkan_driver_funcs = { .p_vulkan_surface_create = X11DRV_vulkan_surface_create, .p_vulkan_surface_destroy = X11DRV_vulkan_surface_destroy, + .p_vulkan_surface_attach = X11DRV_vulkan_surface_attach, .p_vulkan_surface_detach = X11DRV_vulkan_surface_detach, .p_vulkan_surface_presented = X11DRV_vulkan_surface_presented,
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 68b34fa868d..95fa466c994 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1623,7 +1623,7 @@ void detach_client_window( struct x11drv_win_data *data, Window client_window ) /********************************************************************** * attach_client_window */ -static void attach_client_window( struct x11drv_win_data *data, Window client_window ) +void attach_client_window( struct x11drv_win_data *data, Window client_window ) { if (data->client_window == client_window || !client_window) return;
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 0ccffecec49..fa6d58cccab 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -649,6 +649,7 @@ extern void read_net_wm_states( Display *display, struct x11drv_win_data *data ) extern void update_net_wm_states( struct x11drv_win_data *data ); extern void make_window_embedded( struct x11drv_win_data *data ); extern Window create_client_window( HWND hwnd, const XVisualInfo *visual, Colormap colormap ); +extern void attach_client_window( struct x11drv_win_data *data, Window client_window ); extern void detach_client_window( struct x11drv_win_data *data, Window client_window ); extern void destroy_client_window( HWND hwnd, Window client_window ); extern void set_window_visual( struct x11drv_win_data *data, const XVisualInfo *vis, BOOL use_alpha ); diff --git a/include/wine/vulkan_driver.h b/include/wine/vulkan_driver.h index 7ddba4739f4..180cff0e09b 100644 --- a/include/wine/vulkan_driver.h +++ b/include/wine/vulkan_driver.h @@ -21,7 +21,7 @@ #define __WINE_VULKAN_DRIVER_H
/* Wine internal vulkan driver version, needs to be bumped upon vulkan_funcs changes. */ -#define WINE_VULKAN_DRIVER_VERSION 34 +#define WINE_VULKAN_DRIVER_VERSION 35
struct vulkan_funcs { @@ -46,6 +46,7 @@ struct vulkan_driver_funcs { VkResult (*p_vulkan_surface_create)(HWND, VkInstance, VkSurfaceKHR *, void **); void (*p_vulkan_surface_destroy)(HWND, void *); + void (*p_vulkan_surface_attach)(HWND, void *); void (*p_vulkan_surface_detach)(HWND, void *); void (*p_vulkan_surface_presented)(HWND, VkResult);
From: Rémi Bernon rbernon@codeweavers.com
And attach them back when they are. --- dlls/win32u/ntuser_private.h | 1 + dlls/win32u/vulkan.c | 49 +++++++++++++++++++++++++++++++++--- dlls/win32u/window.c | 4 ++- 3 files changed, 49 insertions(+), 5 deletions(-)
diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 04396abca72..7662436a588 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -258,6 +258,7 @@ extern LRESULT system_tray_call( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpar /* vulkan.c */ extern void vulkan_detach_surfaces( struct list *surfaces ); extern void vulkan_set_parent( HWND hwnd, HWND new_parent, HWND old_parent ); +extern void vulkan_set_region( HWND toplevel, HRGN region );
/* window.c */ HANDLE alloc_user_handle( struct user_object *ptr, unsigned int type ); diff --git a/dlls/win32u/vulkan.c b/dlls/win32u/vulkan.c index e57aa440c81..ca681b49d99 100644 --- a/dlls/win32u/vulkan.c +++ b/dlls/win32u/vulkan.c @@ -59,6 +59,7 @@ struct surface struct list entry; VkSurfaceKHR host_surface; void *driver_private; + BOOL is_detached; HWND hwnd; };
@@ -93,6 +94,8 @@ static VkResult win32u_vkCreateWin32SurfaceKHR( VkInstance instance, const VkWin if (!(win = get_win_ptr( toplevel )) || win == WND_DESKTOP || win == WND_OTHER_PROCESS) { driver_funcs->p_vulkan_surface_detach( surface->hwnd, surface->driver_private ); + surface->is_detached = TRUE; + pthread_mutex_lock( &vulkan_mutex ); list_add_tail( &offscreen_surfaces, &surface->entry ); pthread_mutex_unlock( &vulkan_mutex ); @@ -279,7 +282,11 @@ void vulkan_detach_surfaces( struct list *surfaces ) struct surface *surface;
LIST_FOR_EACH_ENTRY( surface, surfaces, struct surface, entry ) + { + if (surface->is_detached) continue; driver_funcs->p_vulkan_surface_detach( surface->hwnd, surface->driver_private ); + surface->is_detached = TRUE; + }
pthread_mutex_lock( &vulkan_mutex ); list_move_tail( &offscreen_surfaces, surfaces ); @@ -288,7 +295,6 @@ void vulkan_detach_surfaces( struct list *surfaces )
static void append_window_surfaces( HWND toplevel, struct list *surfaces ) { - struct surface *surface; WND *win;
if (!(win = get_win_ptr( toplevel )) || win == WND_DESKTOP || win == WND_OTHER_PROCESS) @@ -301,9 +307,6 @@ static void append_window_surfaces( HWND toplevel, struct list *surfaces ) { list_move_tail( &win->vulkan_surfaces, surfaces ); release_win_ptr( win ); - - LIST_FOR_EACH_ENTRY( surface, surfaces, struct surface, entry ) - driver_funcs->p_vulkan_surface_attach( surface->hwnd, surface->driver_private ); } }
@@ -351,12 +354,50 @@ void vulkan_set_parent( HWND hwnd, HWND new_parent, HWND old_parent )
enum_window_surfaces( old_toplevel, hwnd, &surfaces );
+ /* surfaces will be re-attached as needed from surface region updates */ LIST_FOR_EACH_ENTRY( surface, &surfaces, struct surface, entry ) + { + if (surface->is_detached) continue; driver_funcs->p_vulkan_surface_detach( surface->hwnd, surface->driver_private ); + surface->is_detached = TRUE; + }
append_window_surfaces( new_toplevel, &surfaces ); }
+void vulkan_set_region( HWND toplevel, HRGN region ) +{ + struct list surfaces = LIST_INIT(surfaces); + struct surface *surface; + + enum_window_surfaces( toplevel, toplevel, &surfaces ); + + LIST_FOR_EACH_ENTRY( surface, &surfaces, struct surface, entry ) + { + RECT client_rect; + BOOL is_clipped; + + NtUserGetClientRect( surface->hwnd, &client_rect ); + NtUserMapWindowPoints( surface->hwnd, toplevel, (POINT *)&client_rect, 2 ); + is_clipped = NtGdiRectInRegion( region, &client_rect ); + + if (is_clipped && !surface->is_detached) + { + TRACE( "surface %p is now clipped\n", surface->hwnd ); + driver_funcs->p_vulkan_surface_detach( surface->hwnd, surface->driver_private ); + surface->is_detached = TRUE; + } + else if (!is_clipped && surface->is_detached) + { + TRACE( "surface %p is now unclipped\n", surface->hwnd ); + driver_funcs->p_vulkan_surface_attach( surface->hwnd, surface->driver_private ); + surface->is_detached = FALSE; + } + } + + append_window_surfaces( toplevel, &surfaces ); +} + /*********************************************************************** * __wine_get_vulkan_driver (win32u.so) */ diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index f630f107b00..4cef4b71757 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -1762,10 +1762,12 @@ static void update_surface_region( HWND hwnd ) if (status) goto done;
win->surface->funcs->set_region( win->surface, region ); - if (region) NtGdiDeleteObjectApp( region );
done: release_win_ptr( win ); + + vulkan_set_region( hwnd, region ); + if (region) NtGdiDeleteObjectApp( region ); }
/***********************************************************************
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/vulkan.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/dlls/win32u/vulkan.c b/dlls/win32u/vulkan.c index ca681b49d99..63837ab51eb 100644 --- a/dlls/win32u/vulkan.c +++ b/dlls/win32u/vulkan.c @@ -91,6 +91,9 @@ static VkResult win32u_vkCreateWin32SurfaceKHR( VkInstance instance, const VkWin return res; }
+ /* make sure the window has a pixel format selected to get consistent window surface updates */ + if (!win32u_get_window_pixel_format( info->hwnd )) win32u_set_window_pixel_format( info->hwnd, 1, TRUE ); + if (!(win = get_win_ptr( toplevel )) || win == WND_DESKTOP || win == WND_OTHER_PROCESS) { driver_funcs->p_vulkan_surface_detach( surface->hwnd, surface->driver_private );
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/window.c | 54 +++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 14 deletions(-)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 95fa466c994..8dfe8aeaf63 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1456,8 +1456,21 @@ static void sync_client_position( struct x11drv_win_data *data,
if (!data->client_window) return;
- changes.x = data->client_rect.left - data->whole_rect.left; - changes.y = data->client_rect.top - data->whole_rect.top; + if (data->whole_window) + { + changes.x = data->client_rect.left - data->whole_rect.left; + changes.y = data->client_rect.top - data->whole_rect.top; + } + else + { + HWND toplevel = NtUserGetAncestor( data->hwnd, GA_ROOT ); + POINT pos = {data->client_rect.left, data->client_rect.top}; + + NtUserMapWindowPoints( toplevel, toplevel, &pos, 1 ); + changes.x = pos.x; + changes.y = pos.y; + } + changes.width = min( max( 1, data->client_rect.right - data->client_rect.left ), 65535 ); changes.height = min( max( 1, data->client_rect.bottom - data->client_rect.top ), 65535 );
@@ -1610,11 +1623,8 @@ void detach_client_window( struct x11drv_win_data *data, Window client_window )
TRACE( "%p/%lx detaching client window %lx\n", data->hwnd, data->whole_window, client_window );
- if (data->whole_window) - { - client_window_events_disable( data, client_window ); - XReparentWindow( gdi_display, client_window, get_dummy_parent(), 0, 0 ); - } + client_window_events_disable( data, client_window ); + XReparentWindow( gdi_display, client_window, get_dummy_parent(), 0, 0 );
data->client_window = 0; } @@ -1625,18 +1635,33 @@ void detach_client_window( struct x11drv_win_data *data, Window client_window ) */ void attach_client_window( struct x11drv_win_data *data, Window client_window ) { + Window whole_window; + POINT pos = {0}; + if (data->client_window == client_window || !client_window) return;
TRACE( "%p/%lx attaching client window %lx\n", data->hwnd, data->whole_window, client_window );
detach_client_window( data, data->client_window );
- if (data->whole_window) + if ((whole_window = data->whole_window)) + { + pos.x = data->client_rect.left - data->whole_rect.left; + pos.y = data->client_rect.top - data->whole_rect.top; + } + else { - client_window_events_enable( data, client_window ); - XReparentWindow( gdi_display, client_window, data->whole_window, data->client_rect.left - data->whole_rect.left, - data->client_rect.top - data->whole_rect.top ); + HWND toplevel = NtUserGetAncestor( data->hwnd, GA_ROOT ); + whole_window = X11DRV_get_whole_window( toplevel ); + + pos.x = data->client_rect.left; + pos.y = data->client_rect.top; + NtUserMapWindowPoints( toplevel, toplevel, &pos, 1 ); } + if (!whole_window) whole_window = get_dummy_parent(); + + client_window_events_enable( data, client_window ); + XReparentWindow( gdi_display, client_window, whole_window, pos.x, pos.y );
data->client_window = client_window; } @@ -1800,6 +1825,9 @@ static void destroy_whole_window( struct x11drv_win_data *data, BOOL already_des { TRACE( "win %p xwin %lx/%lx\n", data->hwnd, data->whole_window, data->client_window );
+ if (!already_destroyed) detach_client_window( data, data->client_window ); + else if (data->client_window) client_window_events_disable( data, data->client_window ); + if (!data->whole_window) { if (data->embedded) @@ -1821,8 +1849,6 @@ static void destroy_whole_window( struct x11drv_win_data *data, BOOL already_des } else { - if (!already_destroyed) detach_client_window( data, data->client_window ); - else if (data->client_window) client_window_events_disable( data, data->client_window ); XDeleteContext( data->display, data->whole_window, winContext ); if (!already_destroyed) { @@ -1831,7 +1857,7 @@ static void destroy_whole_window( struct x11drv_win_data *data, BOOL already_des } } if (data->whole_colormap) XFreeColormap( data->display, data->whole_colormap ); - data->whole_window = data->client_window = 0; + data->whole_window = 0; data->whole_colormap = 0; data->wm_state = WithdrawnState; data->net_wm_state = 0;
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/window.c | 1 - 1 file changed, 1 deletion(-)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 8dfe8aeaf63..7161ad8077f 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2663,7 +2663,6 @@ BOOL X11DRV_WindowPosChanging( HWND hwnd, HWND insert_after, UINT swp_flags,
if (data->embedded) goto done; if (data->whole_window == root_window) goto done; - if (data->client_window) goto done; if (!client_side_graphics && !layered) goto done;
if (data->surface)
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/vulkan.c | 29 +++++++++++++---------------- dlls/winemac.drv/vulkan.c | 2 +- dlls/winewayland.drv/vulkan.c | 2 +- dlls/winex11.drv/vulkan.c | 4 ++-- include/wine/vulkan_driver.h | 2 +- 5 files changed, 18 insertions(+), 21 deletions(-)
diff --git a/dlls/win32u/vulkan.c b/dlls/win32u/vulkan.c index 63837ab51eb..137b3b58ccb 100644 --- a/dlls/win32u/vulkan.c +++ b/dlls/win32u/vulkan.c @@ -59,7 +59,7 @@ struct surface struct list entry; VkSurfaceKHR host_surface; void *driver_private; - BOOL is_detached; + HDC offscreen_dc; HWND hwnd; };
@@ -96,9 +96,7 @@ static VkResult win32u_vkCreateWin32SurfaceKHR( VkInstance instance, const VkWin
if (!(win = get_win_ptr( toplevel )) || win == WND_DESKTOP || win == WND_OTHER_PROCESS) { - driver_funcs->p_vulkan_surface_detach( surface->hwnd, surface->driver_private ); - surface->is_detached = TRUE; - + driver_funcs->p_vulkan_surface_detach( surface->hwnd, surface->driver_private, &surface->offscreen_dc ); pthread_mutex_lock( &vulkan_mutex ); list_add_tail( &offscreen_surfaces, &surface->entry ); pthread_mutex_unlock( &vulkan_mutex ); @@ -125,6 +123,7 @@ static void win32u_vkDestroySurfaceKHR( VkInstance instance, VkSurfaceKHR handle list_remove( &surface->entry ); pthread_mutex_unlock( &vulkan_mutex );
+ if (surface->offscreen_dc) NtGdiDeleteObjectApp( surface->offscreen_dc ); p_vkDestroySurfaceKHR( instance, surface->host_surface, NULL /* allocator */ ); driver_funcs->p_vulkan_surface_destroy( surface->hwnd, surface->driver_private ); free( surface ); @@ -208,7 +207,7 @@ static void nulldrv_vulkan_surface_attach( HWND hwnd, void *private ) { }
-static void nulldrv_vulkan_surface_detach( HWND hwnd, void *private ) +static void nulldrv_vulkan_surface_detach( HWND hwnd, void *private, HDC *hdc ) { }
@@ -286,9 +285,8 @@ void vulkan_detach_surfaces( struct list *surfaces )
LIST_FOR_EACH_ENTRY( surface, surfaces, struct surface, entry ) { - if (surface->is_detached) continue; - driver_funcs->p_vulkan_surface_detach( surface->hwnd, surface->driver_private ); - surface->is_detached = TRUE; + if (surface->offscreen_dc) continue; + driver_funcs->p_vulkan_surface_detach( surface->hwnd, surface->driver_private, &surface->offscreen_dc ); }
pthread_mutex_lock( &vulkan_mutex ); @@ -360,9 +358,8 @@ void vulkan_set_parent( HWND hwnd, HWND new_parent, HWND old_parent ) /* surfaces will be re-attached as needed from surface region updates */ LIST_FOR_EACH_ENTRY( surface, &surfaces, struct surface, entry ) { - if (surface->is_detached) continue; - driver_funcs->p_vulkan_surface_detach( surface->hwnd, surface->driver_private ); - surface->is_detached = TRUE; + if (surface->offscreen_dc) continue; + driver_funcs->p_vulkan_surface_detach( surface->hwnd, surface->driver_private, &surface->offscreen_dc ); }
append_window_surfaces( new_toplevel, &surfaces ); @@ -384,17 +381,17 @@ void vulkan_set_region( HWND toplevel, HRGN region ) NtUserMapWindowPoints( surface->hwnd, toplevel, (POINT *)&client_rect, 2 ); is_clipped = NtGdiRectInRegion( region, &client_rect );
- if (is_clipped && !surface->is_detached) + if (is_clipped && !surface->offscreen_dc) { TRACE( "surface %p is now clipped\n", surface->hwnd ); - driver_funcs->p_vulkan_surface_detach( surface->hwnd, surface->driver_private ); - surface->is_detached = TRUE; + driver_funcs->p_vulkan_surface_detach( surface->hwnd, surface->driver_private, &surface->offscreen_dc ); } - else if (!is_clipped && surface->is_detached) + else if (!is_clipped && surface->offscreen_dc) { TRACE( "surface %p is now unclipped\n", surface->hwnd ); driver_funcs->p_vulkan_surface_attach( surface->hwnd, surface->driver_private ); - surface->is_detached = FALSE; + NtGdiDeleteObjectApp( surface->offscreen_dc ); + surface->offscreen_dc = NULL; } }
diff --git a/dlls/winemac.drv/vulkan.c b/dlls/winemac.drv/vulkan.c index 9b033a84d57..736d0d397d5 100644 --- a/dlls/winemac.drv/vulkan.c +++ b/dlls/winemac.drv/vulkan.c @@ -182,7 +182,7 @@ static void macdrv_vulkan_surface_attach(HWND hwnd, void *private) { }
-static void macdrv_vulkan_surface_detach(HWND hwnd, void *private) +static void macdrv_vulkan_surface_detach(HWND hwnd, void *private, HDC *hdc) { }
diff --git a/dlls/winewayland.drv/vulkan.c b/dlls/winewayland.drv/vulkan.c index cd63981bf0d..175d8cfa49a 100644 --- a/dlls/winewayland.drv/vulkan.c +++ b/dlls/winewayland.drv/vulkan.c @@ -136,7 +136,7 @@ static void wayland_vulkan_surface_attach(HWND hwnd, void *private) { }
-static void wayland_vulkan_surface_detach(HWND hwnd, void *private) +static void wayland_vulkan_surface_detach(HWND hwnd, void *private, HDC *hdc) { }
diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c index b979da1085e..cf5c44012c8 100644 --- a/dlls/winex11.drv/vulkan.c +++ b/dlls/winex11.drv/vulkan.c @@ -123,12 +123,12 @@ static void X11DRV_vulkan_surface_attach( HWND hwnd, void *private ) } }
-static void X11DRV_vulkan_surface_detach( HWND hwnd, void *private ) +static void X11DRV_vulkan_surface_detach( HWND hwnd, void *private, HDC *hdc ) { Window client_window = (Window)private; struct x11drv_win_data *data;
- TRACE( "%p %p\n", hwnd, private ); + TRACE( "%p %p %p\n", hwnd, private, hdc );
if ((data = get_win_data( hwnd ))) { diff --git a/include/wine/vulkan_driver.h b/include/wine/vulkan_driver.h index 180cff0e09b..74cdcd8987f 100644 --- a/include/wine/vulkan_driver.h +++ b/include/wine/vulkan_driver.h @@ -47,7 +47,7 @@ struct vulkan_driver_funcs VkResult (*p_vulkan_surface_create)(HWND, VkInstance, VkSurfaceKHR *, void **); void (*p_vulkan_surface_destroy)(HWND, void *); void (*p_vulkan_surface_attach)(HWND, void *); - void (*p_vulkan_surface_detach)(HWND, void *); + void (*p_vulkan_surface_detach)(HWND, void *, HDC *); void (*p_vulkan_surface_presented)(HWND, VkResult);
VkBool32 (*p_vkGetPhysicalDeviceWin32PresentationSupportKHR)(VkPhysicalDevice, uint32_t);
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/vulkan.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-)
diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c index cf5c44012c8..df6882271b4 100644 --- a/dlls/winex11.drv/vulkan.c +++ b/dlls/winex11.drv/vulkan.c @@ -37,6 +37,7 @@
#include "wine/debug.h" #include "x11drv.h" +#include "xcomposite.h"
#define VK_NO_PROTOTYPES #define WINE_VK_HOST @@ -74,13 +75,6 @@ static VkResult X11DRV_vulkan_surface_create( HWND hwnd, VkInstance instance, Vk
TRACE( "%p %p %p %p\n", hwnd, instance, surface, private );
- /* TODO: support child window rendering. */ - if (NtUserGetAncestor( hwnd, GA_PARENT ) != NtUserGetDesktopWindow()) - { - FIXME("Application requires child window rendering, which is not implemented yet!\n"); - return VK_ERROR_INCOMPATIBLE_DRIVER; - } - if (!(info.window = create_client_window( hwnd, &default_visual, default_colormap ))) { ERR("Failed to allocate client window for hwnd=%p\n", hwnd); @@ -118,6 +112,9 @@ static void X11DRV_vulkan_surface_attach( HWND hwnd, void *private )
if ((data = get_win_data( hwnd ))) { +#ifdef SONAME_LIBXCOMPOSITE + if (usexcomposite) pXCompositeUnredirectWindow( gdi_display, client_window, CompositeRedirectManual ); +#endif attach_client_window( data, client_window ); release_win_data( data ); } @@ -125,6 +122,8 @@ static void X11DRV_vulkan_surface_attach( HWND hwnd, void *private )
static void X11DRV_vulkan_surface_detach( HWND hwnd, void *private, HDC *hdc ) { + static const WCHAR displayW[] = {'D','I','S','P','L','A','Y'}; + UNICODE_STRING device_str = RTL_CONSTANT_STRING(displayW); Window client_window = (Window)private; struct x11drv_win_data *data;
@@ -135,6 +134,19 @@ static void X11DRV_vulkan_surface_detach( HWND hwnd, void *private, HDC *hdc ) detach_client_window( data, client_window ); release_win_data( data ); } + + if (hdc && (*hdc = NtGdiOpenDCW( &device_str, NULL, NULL, 0, TRUE, NULL, NULL, NULL ))) + { + struct x11drv_escape_set_drawable escape = {0}; + escape.code = X11DRV_SET_DRAWABLE; + escape.mode = IncludeInferiors; + escape.drawable = client_window; + NtUserGetClientRect( hwnd, &escape.dc_rect ); + NtGdiExtEscape( *hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); +#ifdef SONAME_LIBXCOMPOSITE + if (usexcomposite) pXCompositeRedirectWindow( gdi_display, client_window, CompositeRedirectManual ); +#endif + } }
static void X11DRV_vulkan_surface_presented(HWND hwnd, VkResult result)
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/vulkan.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
diff --git a/dlls/win32u/vulkan.c b/dlls/win32u/vulkan.c index 137b3b58ccb..a3c734faf98 100644 --- a/dlls/win32u/vulkan.c +++ b/dlls/win32u/vulkan.c @@ -60,6 +60,7 @@ struct surface VkSurfaceKHR host_surface; void *driver_private; HDC offscreen_dc; + HRGN region; HWND hwnd; };
@@ -107,6 +108,7 @@ static VkResult win32u_vkCreateWin32SurfaceKHR( VkInstance instance, const VkWin release_win_ptr( win ); }
+ surface->region = NtGdiCreateRectRgn( 0, 0, 0, 0 ); surface->hwnd = info->hwnd; *handle = surface_to_handle( surface ); return VK_SUCCESS; @@ -126,6 +128,7 @@ static void win32u_vkDestroySurfaceKHR( VkInstance instance, VkSurfaceKHR handle if (surface->offscreen_dc) NtGdiDeleteObjectApp( surface->offscreen_dc ); p_vkDestroySurfaceKHR( instance, surface->host_surface, NULL /* allocator */ ); driver_funcs->p_vulkan_surface_destroy( surface->hwnd, surface->driver_private ); + NtGdiDeleteObjectApp( surface->region ); free( surface ); }
@@ -144,6 +147,26 @@ static VkResult win32u_vkQueuePresentKHR( VkQueue queue, const VkPresentInfoKHR struct surface *surface = surface_from_handle( surfaces[i] );
driver_funcs->p_vulkan_surface_presented( surface->hwnd, swapchain_res ); + + if (swapchain_res >= VK_SUCCESS && surface->offscreen_dc) + { + UINT width, height; + RECT client_rect; + HDC hdc_dst; + + NtUserGetClientRect( surface->hwnd, &client_rect ); + width = client_rect.right - client_rect.left; + height = client_rect.bottom - client_rect.top; + + WARN("Copying vulkan child window %p rect %s\n", surface->hwnd, wine_dbgstr_rect(&client_rect)); + + if ((hdc_dst = NtUserGetDCEx(surface->hwnd, surface->region, DCX_USESTYLE | DCX_CACHE))) + { + NtGdiStretchBlt(hdc_dst, client_rect.left, client_rect.top, width, height, + surface->offscreen_dc, 0, 0, width, height, SRCCOPY, 0); + NtUserReleaseDC(surface->hwnd, hdc_dst); + } + } }
return res; @@ -385,6 +408,7 @@ void vulkan_set_region( HWND toplevel, HRGN region ) { TRACE( "surface %p is now clipped\n", surface->hwnd ); driver_funcs->p_vulkan_surface_detach( surface->hwnd, surface->driver_private, &surface->offscreen_dc ); + NtGdiCombineRgn( surface->region, region, 0, RGN_COPY ); } else if (!is_clipped && surface->offscreen_dc) {
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=145365
Your paranoid android.
=== debian11b (64 bit WoW report) ===
imm32: imm32: Timeout
Fwiw I have a sample program I use to test various combinations here: https://gitlab.winehq.org/rbernon/wine/-/blob/wip/nulldrv/programs/wglchild/...
The Studio part of Roblox would be a good test case here (because it has a lot of D3D child windows mixed with a Qt5 toolkit and because WineD3D already struggles with it (at least in winex11) because of slow framerate and flickering)