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.
-- v2: win32u: Use GDI blit to implement partial or other process presentation. winex11: Return an offscreen HDC from vulkan_surface_detach. win32u: Pass a HDC parameter to vulkan_surface_detach. winex11: Create a window surface even when there is client window. winex11: Also attach child client windows to their toplevel window. win32u: Make sure vulkan windows have a pixel format selected. win32u: Detach vulkan surfaces that aren't fully visible. win32u: Detach vulkan surfaces that are offscreen or for another process. win32u: Move vulkan surfaces to their new parent when reparenting. win32u: Introduce a new vulkan offscreen surfaces list.
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..7f8a0f716a5 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_unlock( &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_unlock( &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 7f8a0f716a5..2aa61ed565d 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 2aa61ed565d..7ad9b72256e 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 7ad9b72256e..a485cb3ccb8 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 a485cb3ccb8..199dd673c75 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 199dd673c75..5b319e48628 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) {
v2: Fix some `pthread_mutex_lock` typo.
I think I already have the first bug with this MR: ![OpenImageViewer showing a blank screen with DXVK on Wine](/uploads/0fa95f85829680881d6b503a8f83546e/Screenshot_20240503_201710.png)
On Fri May 24 07:02:27 2024 +0000, Aida Jonikienė wrote:
I think I already have the first bug with this MR: ![OpenImageViewer showing a blank screen with DXVK on Wine](/uploads/0fa95f85829680881d6b503a8f83546e/Screenshot_20240503_201710.png)
Sorry but a screenshot isn't very helpful for debugging.
On Fri May 24 07:02:27 2024 +0000, Rémi Bernon wrote:
Sorry but a screenshot isn't very helpful for debugging.
@rbernon Hopefully a +win,+x11drv,+vulkan,+gdi,+win32u,+user32 log is good enough then: [oiv_childwindow_dxvk.log](/uploads/61babe5ea27aee6110e15718024c0b1b/oiv_childwindow_dxvk.log)
On Fri May 24 07:02:27 2024 +0000, Aida Jonikienė wrote:
@rbernon Hopefully a +win,+x11drv,+vulkan,+gdi,+win32u,+user32 log is good enough then: [oiv_childwindow_dxvk.log](/uploads/61babe5ea27aee6110e15718024c0b1b/oiv_childwindow_dxvk.log)
What application are you trying to run?
On Fri May 24 07:02:27 2024 +0000, Rémi Bernon wrote:
What application are you trying to run?
@rbernon It's OpenImageViewer in this case: https://www.openimageviewer.com/
On Fri May 24 07:02:27 2024 +0000, Aida Jonikienė wrote:
@rbernon It's OpenImageViewer in this case: https://www.openimageviewer.com/
After some quick testing your legacy child window patchset (the one that Proton still uses) still has the same blank content issue (I think I very briefly saw the application content once with the legacy patch but I can't really reproduce it anymore)
On Fri May 24 07:02:27 2024 +0000, Aida Jonikienė wrote:
After some quick testing your legacy child window patchset (the one that Proton still uses) still has the same blank content issue (I think I very briefly saw the application content once with the legacy patch but I can't really reproduce it anymore)
[skyrim-skse_loader.gz](/uploads/1d73fd884df829d357aa31f13f0b60e7/skyrim-skse_loader.gz)
here is also a log that run skyrim with skse under dxvk which is normal in old child rendering patch, with this patch, there are no screen render at all.
This is the patch I use for a long time. [winex11.drv-Support-child-window-rendering-for-Vulkan-via-Xcomposite.diff](/uploads/fd6066d6110efdd77a9b49986e36a8b8/winex11.drv-Support-child-window-rendering-for-Vulkan-via-Xcomposite.diff)
On Fri May 24 07:02:27 2024 +0000, Chunhao Hung wrote:
[skyrim-skse_loader.gz](/uploads/1d73fd884df829d357aa31f13f0b60e7/skyrim-skse_loader.gz)
here is also a log that run skyrim with skse under dxvk which is normal in old child rendering patch, with this patch, there are no screen render at all.
This is the patch I use for a long time. [winex11.drv-Support-child-window-rendering-for-Vulkan-via-Xcomposite.diff](/uploads/fd6066d6110efdd77a9b49986e36a8b8/winex11.drv-Support-child-window-rendering-for-Vulkan-via-Xcomposite.diff)
I tried OIV on wine-tkg 9.8 (all patches False except for staging, esync and fsync) + MR5573 with and without DXVK 2.3.1 applied to the wineprefix and pasted the output in the attachment. The results are that the window only updates when the mouse moves (without DXVK) and I get 2 windows (one with a menubar, one without) with DXVK. I don't know if this is helpful (if not, apologies!). If you need other/more information please let me know.
[wine_9.8_OIV.txt](/uploads/4a71b3938918e2c48bb2c50909b6d938/wine_9.8_OIV.txt)
![Screenshot_from_2024-05-07_17-33-46](/uploads/dc30ae70953f90cad53e339fd0211e5c/Screenshot_from_2024-05-07_17-33-46.png)