This does the same as https://gitlab.winehq.org/wine/wine/-/merge_requests/6398 but for Vulkan. It also implements vulkan child window rendering pretty much for free and supersedes https://gitlab.winehq.org/wine/wine/-/merge_requests/5573.
It is not completely optimal and there as missed opportunities to keep child windows on screen when no DPI scaling and no cropping is needed, but it would require to be smarter and have knowledge about the window clipping region, which will probably better be done in win32u. This is exactly the same for OpenGL, and it will probably be solved together.
-- v4: winex11: Implement vulkan DPI scaling and child window rendering. winex11: Move offscreen client window helpers to init.c. winex11: Only update the client window position in sync_client_position. winex11: Update the GL client window size when it is presented. winex11: Update the vulkan surface size when it is presented. winex11: Use a dedicated structure for vulkan surface private data. win32u: Pass vulkan driver private data to vulkan_surface_presented. winevulkan: Use client rect in window DPI instead of monitor DPI.
From: Rémi Bernon rbernon@codeweavers.com
The application might not be per-monitor aware and we will need to scale the vulkan surface ourselves, but rendering in per-monitor DPI unit will otherwise confuse it. --- dlls/winevulkan/vulkan.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-)
diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 53b567e97d8..4980d42204d 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -38,15 +38,6 @@ static PFN_vkEnumerateInstanceVersion p_vkEnumerateInstanceVersion; static PFN_vkEnumerateInstanceExtensionProperties p_vkEnumerateInstanceExtensionProperties;
-/********************************************************************** - * get_win_monitor_dpi - */ -static UINT get_win_monitor_dpi( HWND hwnd ) -{ - return NtUserGetSystemDpiForProcess( NULL ); /* FIXME: get monitor dpi */ -} - - static int window_surface_compare(const void *key, const struct rb_entry *entry) { const struct wine_surface *surface = RB_ENTRY_VALUE(entry, struct wine_surface, window_entry); @@ -1740,7 +1731,7 @@ VkResult wine_vkAcquireNextImage2KHR(VkDevice device_handle, const VkAcquireNext acquire_info_host.swapchain = swapchain->host_swapchain; res = device->funcs.p_vkAcquireNextImage2KHR(device->host_device, &acquire_info_host, image_index);
- if (res == VK_SUCCESS && NtUserGetClientRect(surface->hwnd, &client_rect, get_win_monitor_dpi(surface->hwnd)) && + if (res == VK_SUCCESS && NtUserGetClientRect(surface->hwnd, &client_rect, NtUserGetDpiForWindow(surface->hwnd)) && !extents_equals(&swapchain->extents, &client_rect)) { WARN("Swapchain size %dx%d does not match client rect %s, returning VK_SUBOPTIMAL_KHR\n", @@ -1763,7 +1754,7 @@ VkResult wine_vkAcquireNextImageKHR(VkDevice device_handle, VkSwapchainKHR swapc res = device->funcs.p_vkAcquireNextImageKHR(device->host_device, swapchain->host_swapchain, timeout, semaphore, fence, image_index);
- if (res == VK_SUCCESS && NtUserGetClientRect(surface->hwnd, &client_rect, get_win_monitor_dpi(surface->hwnd)) && + if (res == VK_SUCCESS && NtUserGetClientRect(surface->hwnd, &client_rect, NtUserGetDpiForWindow(surface->hwnd)) && !extents_equals(&swapchain->extents, &client_rect)) { WARN("Swapchain size %dx%d does not match client rect %s, returning VK_SUBOPTIMAL_KHR\n", @@ -1871,7 +1862,7 @@ VkResult wine_vkQueuePresentKHR(VkQueue queue_handle, const VkPresentInfoKHR *pr RECT client_rect;
if (swapchain_res < VK_SUCCESS) continue; - if (!NtUserGetClientRect(surface->hwnd, &client_rect, get_win_monitor_dpi(surface->hwnd))) + if (!NtUserGetClientRect(surface->hwnd, &client_rect, NtUserGetDpiForWindow(surface->hwnd))) { WARN("Swapchain window %p is invalid, returning VK_ERROR_OUT_OF_DATE_KHR\n", surface->hwnd); if (present_info->pResults) present_info->pResults[i] = VK_ERROR_OUT_OF_DATE_KHR; @@ -2234,7 +2225,7 @@ static void adjust_surface_capabilities(struct wine_instance *instance, struct w
/* Update the image extents to match what the Win32 WSI would provide. */ /* FIXME: handle DPI scaling, somehow */ - NtUserGetClientRect(surface->hwnd, &client_rect, get_win_monitor_dpi(surface->hwnd)); + NtUserGetClientRect(surface->hwnd, &client_rect, NtUserGetDpiForWindow(surface->hwnd)); capabilities->minImageExtent.width = client_rect.right - client_rect.left; capabilities->minImageExtent.height = client_rect.bottom - client_rect.top; capabilities->maxImageExtent.width = client_rect.right - client_rect.left;
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/vulkan.c | 8 ++++---- dlls/winemac.drv/vulkan.c | 2 +- dlls/winewayland.drv/vulkan.c | 2 +- dlls/winex11.drv/vulkan.c | 2 +- include/wine/vulkan_driver.h | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/dlls/win32u/vulkan.c b/dlls/win32u/vulkan.c index 471a4459ec3..d285b07b951 100644 --- a/dlls/win32u/vulkan.c +++ b/dlls/win32u/vulkan.c @@ -127,7 +127,7 @@ static VkResult win32u_vkQueuePresentKHR( VkQueue queue, const VkPresentInfoKHR VkResult swapchain_res = present_info->pResults ? present_info->pResults[i] : res; struct surface *surface = surface_from_handle( surfaces[i] );
- driver_funcs->p_vulkan_surface_presented( surface->hwnd, swapchain_res ); + driver_funcs->p_vulkan_surface_presented( surface->hwnd, surface->driver_private, swapchain_res ); }
return res; @@ -191,7 +191,7 @@ static void nulldrv_vulkan_surface_detach( HWND hwnd, void *private ) { }
-static void nulldrv_vulkan_surface_presented( HWND hwnd, VkResult result ) +static void nulldrv_vulkan_surface_presented( HWND hwnd, void *private, VkResult result ) { }
@@ -259,10 +259,10 @@ static void lazydrv_vulkan_surface_detach( HWND hwnd, void *private ) return driver_funcs->p_vulkan_surface_detach( hwnd, private ); }
-static void lazydrv_vulkan_surface_presented( HWND hwnd, VkResult result ) +static void lazydrv_vulkan_surface_presented( HWND hwnd, void *private, VkResult result ) { vulkan_driver_load(); - return driver_funcs->p_vulkan_surface_presented( hwnd, result ); + driver_funcs->p_vulkan_surface_presented( hwnd, private, result ); }
static VkBool32 lazydrv_vkGetPhysicalDeviceWin32PresentationSupportKHR( VkPhysicalDevice device, uint32_t queue ) diff --git a/dlls/winemac.drv/vulkan.c b/dlls/winemac.drv/vulkan.c index d5a3df579d0..3c6c5ee5b7f 100644 --- a/dlls/winemac.drv/vulkan.c +++ b/dlls/winemac.drv/vulkan.c @@ -182,7 +182,7 @@ static void macdrv_vulkan_surface_detach(HWND hwnd, void *private) { }
-static void macdrv_vulkan_surface_presented(HWND hwnd, VkResult result) +static void macdrv_vulkan_surface_presented(HWND hwnd, void *private, VkResult result) { }
diff --git a/dlls/winewayland.drv/vulkan.c b/dlls/winewayland.drv/vulkan.c index 7a075a925b7..25a6117cb45 100644 --- a/dlls/winewayland.drv/vulkan.c +++ b/dlls/winewayland.drv/vulkan.c @@ -132,7 +132,7 @@ static void wayland_vulkan_surface_detach(HWND hwnd, void *private) { }
-static void wayland_vulkan_surface_presented(HWND hwnd, VkResult result) +static void wayland_vulkan_surface_presented(HWND hwnd, void *private, VkResult result) { ensure_window_surface_contents(hwnd); } diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c index 85993bc517a..7d41d482c8e 100644 --- a/dlls/winex11.drv/vulkan.c +++ b/dlls/winex11.drv/vulkan.c @@ -123,7 +123,7 @@ static void X11DRV_vulkan_surface_detach( HWND hwnd, void *private ) } }
-static void X11DRV_vulkan_surface_presented(HWND hwnd, VkResult result) +static void X11DRV_vulkan_surface_presented( HWND hwnd, void *private, VkResult result ) { }
diff --git a/include/wine/vulkan_driver.h b/include/wine/vulkan_driver.h index 7ddba4739f4..3b1f20fda80 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_detach)(HWND, void *); - void (*p_vulkan_surface_presented)(HWND, VkResult); + void (*p_vulkan_surface_presented)(HWND, void *, VkResult);
VkBool32 (*p_vkGetPhysicalDeviceWin32PresentationSupportKHR)(VkPhysicalDevice, uint32_t); const char *(*p_get_host_surface_extension)(void);
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/vulkan.c | 40 +++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-)
diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c index 7d41d482c8e..92c5fe9212f 100644 --- a/dlls/winex11.drv/vulkan.c +++ b/dlls/winex11.drv/vulkan.c @@ -64,15 +64,27 @@ static VkBool32 (*pvkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevi
static const struct vulkan_driver_funcs x11drv_vulkan_driver_funcs;
-static VkResult X11DRV_vulkan_surface_create( HWND hwnd, VkInstance instance, VkSurfaceKHR *surface, void **private ) +struct vulkan_surface +{ + Window window; +}; + +static void vulkan_surface_destroy( HWND hwnd, struct vulkan_surface *surface ) +{ + destroy_client_window( hwnd, surface->window ); + free( surface ); +} + +static VkResult X11DRV_vulkan_surface_create( HWND hwnd, VkInstance instance, VkSurfaceKHR *handle, void **private ) { VkXlibSurfaceCreateInfoKHR info = { .sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, .dpy = gdi_display, }; + struct vulkan_surface *surface;
- TRACE( "%p %p %p %p\n", hwnd, instance, surface, private ); + TRACE( "%p %p %p %p\n", hwnd, instance, handle, private );
/* TODO: support child window rendering. */ if (NtUserGetAncestor( hwnd, GA_PARENT ) != NtUserGetDesktopWindow()) @@ -81,37 +93,45 @@ static VkResult X11DRV_vulkan_surface_create( HWND hwnd, VkInstance instance, Vk return VK_ERROR_INCOMPATIBLE_DRIVER; }
- if (!(info.window = create_client_window( hwnd, &default_visual, default_colormap ))) + if (!(surface = calloc(1, sizeof(*surface)))) + { + ERR("Failed to allocate vulkan surface for hwnd=%p\n", hwnd); + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + if (!(surface->window = create_client_window( hwnd, &default_visual, default_colormap ))) { ERR("Failed to allocate client window for hwnd=%p\n", hwnd); + free( surface ); return VK_ERROR_OUT_OF_HOST_MEMORY; }
- if (pvkCreateXlibSurfaceKHR( instance, &info, NULL /* allocator */, surface )) + info.window = surface->window; + if (pvkCreateXlibSurfaceKHR( instance, &info, NULL /* allocator */, handle )) { ERR("Failed to create Xlib surface\n"); - destroy_client_window( hwnd, info.window ); + vulkan_surface_destroy( hwnd, surface ); return VK_ERROR_OUT_OF_HOST_MEMORY; }
- *private = (void *)info.window; + *private = (void *)surface;
- TRACE("Created surface 0x%s, private %p\n", wine_dbgstr_longlong(*surface), *private); + TRACE("Created surface 0x%s, private %p\n", wine_dbgstr_longlong(*handle), *private); return VK_SUCCESS; }
static void X11DRV_vulkan_surface_destroy( HWND hwnd, void *private ) { - Window client_window = (Window)private; + struct vulkan_surface *surface = private;
TRACE( "%p %p\n", hwnd, private );
- destroy_client_window( hwnd, client_window ); + vulkan_surface_destroy( hwnd, surface ); }
static void X11DRV_vulkan_surface_detach( HWND hwnd, void *private ) { - Window client_window = (Window)private; + struct vulkan_surface *surface = private; + Window client_window = surface->window; struct x11drv_win_data *data;
TRACE( "%p %p\n", hwnd, private );
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/vulkan.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c index 92c5fe9212f..dbfc7a928ec 100644 --- a/dlls/winex11.drv/vulkan.c +++ b/dlls/winex11.drv/vulkan.c @@ -67,6 +67,7 @@ static const struct vulkan_driver_funcs x11drv_vulkan_driver_funcs; struct vulkan_surface { Window window; + RECT rect; };
static void vulkan_surface_destroy( HWND hwnd, struct vulkan_surface *surface ) @@ -104,6 +105,7 @@ static VkResult X11DRV_vulkan_surface_create( HWND hwnd, VkInstance instance, Vk free( surface ); return VK_ERROR_OUT_OF_HOST_MEMORY; } + NtUserGetClientRect( hwnd, &surface->rect, NtUserGetDpiForWindow( hwnd ) );
info.window = surface->window; if (pvkCreateXlibSurfaceKHR( instance, &info, NULL /* allocator */, handle )) @@ -143,8 +145,24 @@ static void X11DRV_vulkan_surface_detach( HWND hwnd, void *private ) } }
+static void vulkan_surface_update_size( HWND hwnd, struct vulkan_surface *surface ) +{ + XWindowChanges changes; + RECT rect; + + NtUserGetClientRect( hwnd, &rect, NtUserGetDpiForWindow( hwnd ) ); + if (EqualRect( &surface->rect, &rect )) return; + + changes.width = min( max( 1, rect.right ), 65535 ); + changes.height = min( max( 1, rect.bottom ), 65535 ); + XConfigureWindow( gdi_display, surface->window, CWWidth | CWHeight, &changes ); + surface->rect = rect; +} + static void X11DRV_vulkan_surface_presented( HWND hwnd, void *private, VkResult result ) { + struct vulkan_surface *surface = private; + vulkan_surface_update_size( hwnd, surface ); }
static VkBool32 X11DRV_vkGetPhysicalDeviceWin32PresentationSupportKHR(VkPhysicalDevice phys_dev,
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/opengl.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index 8ee0a824753..eee57f6b804 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -2840,6 +2840,7 @@ static void update_gl_drawable_size( struct gl_drawable *gl )
switch (gl->type) { + case DC_GL_WINDOW: case DC_GL_CHILD_WIN: gl->rect = rect; XConfigureWindow( gdi_display, gl->window, CWWidth | CWHeight, &changes );
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/window.c | 5 ----- 1 file changed, 5 deletions(-)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 8d795581f83..69b38bef5b6 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1387,13 +1387,8 @@ static void sync_client_position( struct x11drv_win_data *data, const struct win
changes.x = data->rects.client.left - data->rects.visible.left; changes.y = data->rects.client.top - data->rects.visible.top; - changes.width = min( max( 1, data->rects.client.right - data->rects.client.left ), 65535 ); - changes.height = min( max( 1, data->rects.client.bottom - data->rects.client.top ), 65535 ); - if (changes.x != old_rects->client.left - old_rects->visible.left) mask |= CWX; if (changes.y != old_rects->client.top - old_rects->visible.top) mask |= CWY; - if (changes.width != old_rects->client.right - old_rects->client.left) mask |= CWWidth; - if (changes.height != old_rects->client.bottom - old_rects->client.top) mask |= CWHeight;
if (mask) {
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/init.c | 62 +++++++++++++++++++++++++++++++++++++ dlls/winex11.drv/opengl.c | 64 --------------------------------------- dlls/winex11.drv/x11drv.h | 5 +++ 3 files changed, 67 insertions(+), 64 deletions(-)
diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c index ea19be92b74..e7a6132fd87 100644 --- a/dlls/winex11.drv/init.c +++ b/dlls/winex11.drv/init.c @@ -194,6 +194,68 @@ static HFONT X11DRV_SelectFont( PHYSDEV dev, HFONT hfont, UINT *aa_flags ) return dev->funcs->pSelectFont( dev, hfont, aa_flags ); }
+BOOL needs_offscreen_rendering( HWND hwnd, BOOL known_child ) +{ + if (NtUserGetDpiForWindow( hwnd ) != get_win_monitor_dpi( hwnd )) return TRUE; /* needs DPI scaling */ + if (NtUserGetAncestor( hwnd, GA_PARENT ) != NtUserGetDesktopWindow()) return TRUE; /* child window, needs compositing */ + if (NtUserGetWindowRelative( hwnd, GW_CHILD )) return TRUE; /* window has children, needs compositing */ + if (known_child) return TRUE; /* window is/have children, needs compositing */ + return FALSE; +} + +void set_dc_drawable( HDC hdc, Drawable drawable, const RECT *rect, int mode ) +{ + struct x11drv_escape_set_drawable escape = + { + .code = X11DRV_SET_DRAWABLE, + .drawable = drawable, + .dc_rect = *rect, + .mode = mode, + }; + NtGdiExtEscape( hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); +} + +Drawable get_dc_drawable( HDC hdc, RECT *rect ) +{ + struct x11drv_escape_get_drawable escape = {.code = X11DRV_GET_DRAWABLE}; + NtGdiExtEscape( hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, sizeof(escape), (LPSTR)&escape ); + *rect = escape.dc_rect; + return escape.drawable; +} + +HRGN get_dc_monitor_region( HWND hwnd, HDC hdc ) +{ + RGNDATA *data; + UINT i, size; + HRGN region; + POINT pt; + + if (!(region = NtGdiCreateRectRgn( 0, 0, 0, 0 ))) return 0; + if (NtGdiGetRandomRgn( hdc, region, SYSRGN ) <= 0) goto failed; + if (!(size = NtGdiGetRegionData( region, 0, NULL ))) goto failed; + if (!(data = malloc( size ))) goto failed; + NtGdiGetRegionData( region, size, data ); + NtGdiDeleteObjectApp( region ); + + NtGdiGetDCPoint( hdc, NtGdiGetDCOrg, &pt ); + NtUserLogicalToPerMonitorDPIPhysicalPoint( hwnd, &pt ); + for (i = 0; i < data->rdh.nCount; i++) + { + RECT *rect = (RECT *)data->Buffer + i; + NtUserLogicalToPerMonitorDPIPhysicalPoint( hwnd, (POINT *)&rect->left ); + NtUserLogicalToPerMonitorDPIPhysicalPoint( hwnd, (POINT *)&rect->right ); + OffsetRect( rect, -pt.x, -pt.y ); + } + + region = NtGdiExtCreateRegion( NULL, size, data ); + free( data ); + return region; + +failed: + NtGdiDeleteObjectApp( region ); + return 0; +} + /********************************************************************** * ExtEscape (X11DRV.@) */ diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index eee57f6b804..53bad9efb23 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -1090,29 +1090,6 @@ static GLXContext create_glxcontext(Display *display, struct wgl_context *contex return ctx; }
-static void set_dc_drawable( HDC hdc, Drawable drawable, const RECT *rect, int mode ) -{ - struct x11drv_escape_set_drawable escape = - { - .code = X11DRV_SET_DRAWABLE, - .drawable = drawable, - .dc_rect = *rect, - .mode = mode, - }; - NtGdiExtEscape( hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); -} - -static BOOL needs_offscreen_rendering( HWND hwnd, BOOL known_child ) -{ - if (NtUserGetDpiForWindow( hwnd ) != get_win_monitor_dpi( hwnd )) return TRUE; - - if (!known_child && !NtUserGetWindowRelative( hwnd, GW_CHILD ) && - NtUserGetAncestor( hwnd, GA_PARENT ) == NtUserGetDesktopWindow()) - return FALSE; /* childless top-level window */ - - return TRUE; -} - /*********************************************************************** * create_gl_drawable */ @@ -1865,47 +1842,6 @@ static BOOL glxdrv_wglShareLists(struct wgl_context *org, struct wgl_context *de return TRUE; }
-static Drawable get_dc_drawable( HDC hdc, RECT *rect ) -{ - struct x11drv_escape_get_drawable escape = {.code = X11DRV_GET_DRAWABLE}; - NtGdiExtEscape( hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, sizeof(escape), (LPSTR)&escape ); - *rect = escape.dc_rect; - return escape.drawable; -} - -static HRGN get_dc_monitor_region( HWND hwnd, HDC hdc ) -{ - RGNDATA *data; - UINT i, size; - HRGN region; - POINT pt; - - if (!(region = NtGdiCreateRectRgn( 0, 0, 0, 0 ))) return 0; - if (NtGdiGetRandomRgn( hdc, region, SYSRGN ) <= 0) goto failed; - if (!(size = NtGdiGetRegionData( region, 0, NULL ))) goto failed; - if (!(data = malloc( size ))) goto failed; - NtGdiGetRegionData( region, size, data ); - NtGdiDeleteObjectApp( region ); - - NtGdiGetDCPoint( hdc, NtGdiGetDCOrg, &pt ); - NtUserLogicalToPerMonitorDPIPhysicalPoint( hwnd, &pt ); - for (i = 0; i < data->rdh.nCount; i++) - { - RECT *rect = (RECT *)data->Buffer + i; - NtUserLogicalToPerMonitorDPIPhysicalPoint( hwnd, (POINT *)&rect->left ); - NtUserLogicalToPerMonitorDPIPhysicalPoint( hwnd, (POINT *)&rect->right ); - OffsetRect( rect, -pt.x, -pt.y ); - } - - region = NtGdiExtCreateRegion( NULL, size, data ); - free( data ); - return region; - -failed: - NtGdiDeleteObjectApp( region ); - return 0; -} - static void present_gl_drawable( HWND hwnd, HDC hdc, struct gl_drawable *gl, BOOL flush ) { HWND toplevel = NtUserGetAncestor( hwnd, GA_ROOT ); diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index bffcd196d04..10257898a76 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -353,6 +353,11 @@ struct x11drv_escape_get_drawable RECT dc_rect; /* DC rectangle relative to drawable */ };
+extern BOOL needs_offscreen_rendering( HWND hwnd, BOOL known_child ); +extern void set_dc_drawable( HDC hdc, Drawable drawable, const RECT *rect, int mode ); +extern Drawable get_dc_drawable( HDC hdc, RECT *rect ); +extern HRGN get_dc_monitor_region( HWND hwnd, HDC hdc ); + /************************************************************************** * X11 USER driver */
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/vulkan.c | 91 ++++++++++++++++++++++++++++++++++++--- dlls/winex11.drv/window.c | 2 +- dlls/winex11.drv/x11drv.h | 1 + 3 files changed, 86 insertions(+), 8 deletions(-)
diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c index dbfc7a928ec..59714b522fb 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 @@ -68,11 +69,17 @@ struct vulkan_surface { Window window; RECT rect; + + BOOL offscreen; + HDC hdc_src; + HDC hdc_dst; };
static void vulkan_surface_destroy( HWND hwnd, struct vulkan_surface *surface ) { destroy_client_window( hwnd, surface->window ); + if (surface->hdc_dst) NtGdiDeleteObjectApp( surface->hdc_dst ); + if (surface->hdc_src) NtGdiDeleteObjectApp( surface->hdc_src ); free( surface ); }
@@ -87,13 +94,6 @@ static VkResult X11DRV_vulkan_surface_create( HWND hwnd, VkInstance instance, Vk
TRACE( "%p %p %p %p\n", hwnd, instance, handle, 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 (!(surface = calloc(1, sizeof(*surface)))) { ERR("Failed to allocate vulkan surface for hwnd=%p\n", hwnd); @@ -159,10 +159,87 @@ static void vulkan_surface_update_size( HWND hwnd, struct vulkan_surface *surfac surface->rect = rect; }
+static void vulkan_surface_update_offscreen( HWND hwnd, struct vulkan_surface *surface ) +{ + BOOL offscreen = needs_offscreen_rendering( hwnd, FALSE ); + struct x11drv_win_data *data; + + if (offscreen == surface->offscreen) return; + surface->offscreen = offscreen; + + if (!surface->offscreen) + { +#ifdef SONAME_LIBXCOMPOSITE + if (usexcomposite) pXCompositeUnredirectWindow( gdi_display, surface->window, CompositeRedirectManual ); +#endif + if (surface->hdc_dst) + { + NtGdiDeleteObjectApp( surface->hdc_dst ); + surface->hdc_dst = NULL; + } + if (surface->hdc_src) + { + NtGdiDeleteObjectApp( surface->hdc_src ); + surface->hdc_src = NULL; + } + } + else + { + static const WCHAR displayW[] = {'D','I','S','P','L','A','Y'}; + UNICODE_STRING device_str = RTL_CONSTANT_STRING(displayW); + surface->hdc_dst = NtGdiOpenDCW( &device_str, NULL, NULL, 0, TRUE, NULL, NULL, NULL ); + surface->hdc_src = NtGdiOpenDCW( &device_str, NULL, NULL, 0, TRUE, NULL, NULL, NULL ); + set_dc_drawable( surface->hdc_src, surface->window, &surface->rect, IncludeInferiors ); +#ifdef SONAME_LIBXCOMPOSITE + if (usexcomposite) pXCompositeRedirectWindow( gdi_display, surface->window, CompositeRedirectManual ); +#endif + } + + if ((data = get_win_data( hwnd ))) + { + if (surface->offscreen) detach_client_window( data, surface->window ); + else attach_client_window( data, surface->window ); + release_win_data( data ); + } +} + static void X11DRV_vulkan_surface_presented( HWND hwnd, void *private, VkResult result ) { struct vulkan_surface *surface = private; + HWND toplevel = NtUserGetAncestor( hwnd, GA_ROOT ); + struct x11drv_win_data *data; + RECT rect_dst, rect; + Drawable window; + HRGN region; + HDC hdc; + vulkan_surface_update_size( hwnd, surface ); + vulkan_surface_update_offscreen( hwnd, surface ); + + if (!surface->offscreen) return; + if (!(hdc = NtUserGetDCEx( hwnd, 0, DCX_CACHE | DCX_CLIPCHILDREN ))) return; + window = X11DRV_get_whole_window( toplevel ); + region = get_dc_monitor_region( hwnd, hdc ); + + NtUserGetClientRect( hwnd, &rect_dst, get_win_monitor_dpi( hwnd ) ); + NtUserMapWindowPoints( hwnd, toplevel, (POINT *)&rect_dst, 2, get_win_monitor_dpi( hwnd ) ); + + if ((data = get_win_data( toplevel ))) + { + OffsetRect( &rect_dst, data->rects.client.left - data->rects.visible.left, + data->rects.client.top - data->rects.visible.top ); + release_win_data( data ); + } + + if (get_dc_drawable( surface->hdc_dst, &rect ) != window || !EqualRect( &rect, &rect_dst )) + set_dc_drawable( surface->hdc_dst, window, &rect_dst, ClipByChildren ); + if (region) NtGdiExtSelectClipRgn( surface->hdc_dst, region, RGN_COPY ); + + NtGdiStretchBlt( surface->hdc_dst, 0, 0, rect_dst.right - rect_dst.left, rect_dst.bottom - rect_dst.top, + surface->hdc_src, 0, 0, surface->rect.right, surface->rect.bottom, SRCCOPY, 0 ); + + if (region) NtGdiDeleteObjectApp( region ); + if (hdc) NtGdiDeleteObjectApp( hdc ); }
static VkBool32 X11DRV_vkGetPhysicalDeviceWin32PresentationSupportKHR(VkPhysicalDevice phys_dev, diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 69b38bef5b6..c99602975b5 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1546,7 +1546,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 10257898a76..a207db1c4a1 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -640,6 +640,7 @@ 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 detach_client_window( struct x11drv_win_data *data, Window client_window ); +extern void attach_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 ); extern void change_systray_owner( Display *display, Window systray_window );
v2: Fix macOS build by moving the offscreen helpers that were conditionally built with OpenGL to a separate source, make the vulkan offscreen surface nature updatable as the surface isn't recreated and cannot be notified of new parent/children otherwise.
Is there something wrong with this? It's pretty much the same thing as for OpenGL.
On Fri Sep 13 12:24:35 2024 +0000, Rémi Bernon wrote:
Is there something wrong with this? It's pretty much the same thing as for OpenGL.
If it's similar to the OpenGL approach then does that mean it suffers from the some of the same issues (like poor performance and flickering content with multiple D3D swapchains in a single window)?
On Fri Sep 13 12:24:35 2024 +0000, Aida Jonikienė wrote:
If it's similar to the OpenGL approach then does that mean it suffers from the some of the same issues (like poor performance and flickering content with multiple D3D swapchains in a single window)?
If that's the case with GL, then probably. Still better than nothing at all and as they both do the same thing, problems could later be solved the same way for both.
On Fri Sep 13 13:56:40 2024 +0000, Rémi Bernon wrote:
If that's the case with GL, then probably. Still better than nothing at all and as they both do the same thing, problems could later be solved the same way for both.
There wasn't similar things in GL, the API is a bit different, setting a new drawable on the GL context was obsoleting the old one (without a possibility to switch back).
I think having multiple vulkan surfaces on a window always resulting in offscreen blitting is too bad actually. This is not so uncommon case in practice, and having any surface created and left resulting in the whole game rendering going through blitting is very unfortunate. Note that it is not what we have in Proton currently: in Proton now we don't blit those but attach client window back if currently offscreen surface is presented. This is also hacky of course but at least works, assuming the apps do not switch presenting to surfaces after each frame (not yet encountered). Offscreen blitting has the issues mentioned above, to mention another, HDR won't work where it currently works in Proton + Gamescope.
On Fri Sep 13 15:10:20 2024 +0000, Paul Gofman wrote:
There wasn't similar things in GL, the API is a bit different, setting a new drawable on the GL context was obsoleting the old one (without a possibility to switch back). I think having multiple vulkan surfaces on a window always resulting in offscreen blitting is too bad actually. This is not so uncommon case in practice, and having any surface created and left resulting in the whole game rendering going through blitting is very unfortunate. Note that it is not what we have in Proton currently: in Proton now we don't blit those but attach client window back if currently offscreen surface is presented. This is also hacky of course but at least works, assuming the apps do not switch presenting to surfaces after each frame (not yet encountered). Offscreen blitting has the issues mentioned above, to mention another, HDR won't work where it currently works in Proton + Gamescope.
This is not what this is doing: this only creates offscreen surfaces for child windows (which currently aren't supported at all), or when DPI scaling is required (in which case we need to scale the surfaces, and that's not doable without indirection). It doesn't change anything wrt. multiple surfaces.
Note that on that matter, upstream simply doesn't allow multiple surfaces to be created on the same window, and any new surface that is created invalidates the previous ones, which I think matches some tests we have.