From: Namo Nath <nn.git@tuta.io> Fixes late-bound transparency in frameworks like .NET/WinForms by allowing Vulkan swapchains to upgrade to ARGB modes when DWM glass effects are enabled post-initialization. - State Synchronization: Adds p_get_vulkan_surface_alpha_state to vulkan_driver_funcs to safely query DWM/Glass state from the window manager driver. - Late Invalidation & Upgrade: win32u now caches the DWM alpha requirement. win32u_vkAcquireNextImageKHR detects state mismatches via the non-blocking driver callback and returns VK_ERROR_OUT_OF_DATE_KHR. This forces client recreation, allowing win32u_vkCreateSwapchainKHR to override compositeAlpha to PRE_MULTIPLIED. --- dlls/win32u/vulkan.c | 79 +++++++++++++++++++++++++++++++++-- dlls/winewayland.drv/vulkan.c | 15 +++++++ dlls/winex11.drv/vulkan.c | 16 +++++++ include/wine/vulkan_driver.h | 1 + 4 files changed, 107 insertions(+), 4 deletions(-) diff --git a/dlls/win32u/vulkan.c b/dlls/win32u/vulkan.c index 13981410e94..09aec627634 100644 --- a/dlls/win32u/vulkan.c +++ b/dlls/win32u/vulkan.c @@ -143,6 +143,7 @@ struct surface struct vulkan_surface obj; struct client_surface *client; HWND hwnd; + BOOL dwm_alpha; }; static struct surface *surface_from_handle( VkSurfaceKHR handle ) @@ -156,6 +157,7 @@ struct swapchain struct vulkan_swapchain obj; struct surface *surface; VkExtent2D extents; + BOOL dwm_alpha; }; static struct swapchain *swapchain_from_handle( VkSwapchainKHR handle ) @@ -1616,6 +1618,21 @@ static VkResult win32u_vkGetPhysicalDeviceSurfaceCapabilitiesKHR( VkPhysicalDevi res = instance->p_vkGetPhysicalDeviceSurfaceCapabilitiesKHR( physical_device->host.physical_device, surface->obj.host.surface, capabilities ); if (!res) adjust_surface_capabilities( instance, surface, capabilities ); + + /* Sync target state via asynchronous driver callback */ + if (!res && driver_funcs->p_get_vulkan_surface_alpha_state) + { + surface->dwm_alpha = driver_funcs->p_get_vulkan_surface_alpha_state(surface->hwnd); + + /* Advertise transparency support if DWM Glass is requested */ + if (surface->dwm_alpha) + { + capabilities->supportedCompositeAlpha |= VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR | + VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR; + TRACE("Advertising supportedCompositeAlpha for HWND %p.\n", surface->hwnd); + } + } + return res; } @@ -1642,6 +1659,21 @@ static VkResult win32u_vkGetPhysicalDeviceSurfaceCapabilities2KHR( VkPhysicalDev res = instance->p_vkGetPhysicalDeviceSurfaceCapabilities2KHR( physical_device->host.physical_device, &surface_info_host, capabilities ); if (!res) adjust_surface_capabilities( instance, surface, &capabilities->surfaceCapabilities ); + + /* Sync target state via asynchronous driver callback */ + if (!res && driver_funcs->p_get_vulkan_surface_alpha_state) + { + surface->dwm_alpha = driver_funcs->p_get_vulkan_surface_alpha_state(surface->hwnd); + + /* Advertise transparency support if DWM Glass is requested */ + if (surface->dwm_alpha) + { + capabilities->surfaceCapabilities.supportedCompositeAlpha |= VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR | + VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR; + TRACE("Advertising v2 supportedCompositeAlpha for HWND %p.\n", surface->hwnd); + } + } + return res; } @@ -1763,7 +1795,7 @@ static VkResult win32u_vkGetPhysicalDeviceSurfaceFormatsKHR( VkPhysicalDevice cl struct vulkan_instance *instance = physical_device->instance; return instance->p_vkGetPhysicalDeviceSurfaceFormatsKHR( physical_device->host.physical_device, - surface->obj.host.surface, format_count, formats ); + surface->obj.host.surface, format_count, formats ); } static VkResult win32u_vkGetPhysicalDeviceSurfaceFormats2KHR( VkPhysicalDevice client_physical_device, const VkPhysicalDeviceSurfaceInfo2KHR *surface_info, @@ -1787,7 +1819,7 @@ static VkResult win32u_vkGetPhysicalDeviceSurfaceFormats2KHR( VkPhysicalDevice c surface_formats = calloc( *format_count, sizeof(*surface_formats) ); if (!surface_formats) return VK_ERROR_OUT_OF_HOST_MEMORY; - res = win32u_vkGetPhysicalDeviceSurfaceFormatsKHR( client_physical_device, surface_info->surface, format_count, surface_formats ); + res = win32u_vkGetPhysicalDeviceSurfaceFormatsKHR( client_physical_device, surface_info->surface, format_count, surface_formats ); if (!res || res == VK_INCOMPLETE) for (i = 0; i < *format_count; i++) formats[i].surfaceFormat = surface_formats[i]; free( surface_formats ); @@ -1797,7 +1829,7 @@ static VkResult win32u_vkGetPhysicalDeviceSurfaceFormats2KHR( VkPhysicalDevice c surface_info_host.surface = surface->obj.host.surface; return instance->p_vkGetPhysicalDeviceSurfaceFormats2KHR( physical_device->host.physical_device, - &surface_info_host, format_count, formats ); + &surface_info_host, format_count, formats ); } static VkBool32 win32u_vkGetPhysicalDeviceWin32PresentationSupportKHR( VkPhysicalDevice client_physical_device, uint32_t queue ) @@ -1855,6 +1887,27 @@ static VkResult win32u_vkCreateSwapchainKHR( VkDevice client_device, const VkSwa create_info_host.pNext = &scaling; } + /* Sync target state via asynchronous driver callback during creation */ + if (driver_funcs->p_get_vulkan_surface_alpha_state) + { + surface->dwm_alpha = driver_funcs->p_get_vulkan_surface_alpha_state(surface->hwnd); + } + + /* Force transparent composite alpha for Wayland overlay */ + if (surface->dwm_alpha && create_info_host.compositeAlpha == VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) + { + if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR) + { + create_info_host.compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR; + TRACE("Upgraded swapchain compositeAlpha from OPAQUE to PRE_MULTIPLIED for HWND %p.\n", surface->hwnd); + } + else if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR) + { + create_info_host.compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR; + TRACE("Upgraded swapchain compositeAlpha from OPAQUE to POST_MULTIPLIED for HWND %p.\n", surface->hwnd); + } + } + if (!(swapchain = calloc( 1, sizeof(*swapchain) ))) return VK_ERROR_OUT_OF_HOST_MEMORY; if ((res = device->p_vkCreateSwapchainKHR( device->host.device, &create_info_host, NULL, &host_swapchain ))) @@ -1866,6 +1919,10 @@ static VkResult win32u_vkCreateSwapchainKHR( VkDevice client_device, const VkSwa vulkan_object_init( &swapchain->obj.obj, host_swapchain ); swapchain->surface = surface; swapchain->extents = create_info->imageExtent; + + /* Track if this swapchain was successfully built with alpha */ + swapchain->dwm_alpha = (create_info_host.compositeAlpha != VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR); + instance->p_insert_object( instance, &swapchain->obj.obj ); *ret = swapchain->obj.client.swapchain; @@ -1927,12 +1984,26 @@ static VkResult win32u_vkAcquireNextImageKHR( VkDevice client_device, VkSwapchai RECT client_rect; VkResult res; + /* Evaluate driver state without synchronous IPC blocking. + * The driver func should read an atomic flag or cached window state updated asynchronously. */ + if (!swapchain->dwm_alpha && driver_funcs->p_get_vulkan_surface_alpha_state) + { + surface->dwm_alpha = driver_funcs->p_get_vulkan_surface_alpha_state(surface->hwnd); + } + + /* Force DXVK to recreate swapchain if DWM transparency was late-bound */ + if (surface->dwm_alpha != swapchain->dwm_alpha) + { + TRACE("DWM alpha mismatch detected for HWND %p. Requesting recreation.\n", surface->hwnd); + return VK_ERROR_OUT_OF_DATE_KHR; + } + res = device->p_vkAcquireNextImageKHR( device->host.device, swapchain->obj.host.swapchain, timeout, semaphore ? semaphore->host.semaphore : 0, fence ? fence->host.fence : 0, image_index ); if (!res && get_surface_rect( surface->hwnd, &client_rect, NtUserGetDpiForWindow( surface->hwnd ) ) && - !extents_equals( &swapchain->extents, &client_rect )) + !extents_equals( &swapchain->extents, &client_rect )) { WARN( "Swapchain size %dx%d does not match client rect %s, returning VK_SUBOPTIMAL_KHR\n", swapchain->extents.width, swapchain->extents.height, wine_dbgstr_rect( &client_rect ) ); diff --git a/dlls/winewayland.drv/vulkan.c b/dlls/winewayland.drv/vulkan.c index f8fd372757d..bad058ed4a0 100644 --- a/dlls/winewayland.drv/vulkan.c +++ b/dlls/winewayland.drv/vulkan.c @@ -97,12 +97,27 @@ static void wayland_map_device_extensions(struct vulkan_device_extensions *exten if (extensions->has_VK_KHR_external_fence_fd) extensions->has_VK_KHR_external_fence_win32 = 1; } +static BOOL wayland_vulkan_get_surface_alpha_state(HWND hwnd) +{ + struct wayland_win_data *data; + BOOL is_alpha = FALSE; + + if ((data = wayland_win_data_get(hwnd))) + { + is_alpha = (data->dwm_mode == WAYLAND_DWM_EXTEND_GLASS); + wayland_win_data_release(data); + } + + return is_alpha; +} + static const struct vulkan_driver_funcs wayland_vulkan_driver_funcs = { .p_vulkan_surface_create = wayland_vulkan_surface_create, .p_get_physical_device_presentation_support = wayland_get_physical_device_presentation_support, .p_map_instance_extensions = wayland_map_instance_extensions, .p_map_device_extensions = wayland_map_device_extensions, + .p_get_vulkan_surface_alpha_state = wayland_vulkan_get_surface_alpha_state, }; /********************************************************************** diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c index 421971178a7..642c1041b78 100644 --- a/dlls/winex11.drv/vulkan.c +++ b/dlls/winex11.drv/vulkan.c @@ -94,12 +94,28 @@ static void X11DRV_map_device_extensions( struct vulkan_device_extensions *exten if (extensions->has_VK_KHR_external_fence_fd) extensions->has_VK_KHR_external_fence_win32 = 1; } +static BOOL x11_vulkan_get_surface_alpha_state(HWND hwnd) +{ + struct x11drv_win_data *data; + BOOL is_alpha = FALSE; + + if ((data = get_win_data(hwnd))) + { + is_alpha = data->dwm_glass_state; + release_win_data(data); + } + + return is_alpha; +} + + static const struct vulkan_driver_funcs x11drv_vulkan_driver_funcs = { .p_vulkan_surface_create = X11DRV_vulkan_surface_create, .p_get_physical_device_presentation_support = X11DRV_get_physical_device_presentation_support, .p_map_instance_extensions = X11DRV_map_instance_extensions, .p_map_device_extensions = X11DRV_map_device_extensions, + .p_get_vulkan_surface_alpha_state = x11_vulkan_get_surface_alpha_state, }; UINT X11DRV_VulkanInit( UINT version, void *vulkan_handle, const struct vulkan_driver_funcs **driver_funcs ) diff --git a/include/wine/vulkan_driver.h b/include/wine/vulkan_driver.h index 9c18f6d20db..c69e37f2a7f 100644 --- a/include/wine/vulkan_driver.h +++ b/include/wine/vulkan_driver.h @@ -362,6 +362,7 @@ struct vulkan_driver_funcs VkBool32 (*p_get_physical_device_presentation_support)(struct vulkan_physical_device *, uint32_t); void (*p_map_instance_extensions)( struct vulkan_instance_extensions *extensions ); void (*p_map_device_extensions)( struct vulkan_device_extensions *extensions ); + BOOL (*p_get_vulkan_surface_alpha_state)(HWND); }; #endif /* WINE_UNIX_LIB */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10180