[PATCH v2 0/2] MR8718: win32u: Create Vulkan swapchains with VkSwapchainPresentScalingCreateInfoEXT when the surface will be scaled.
If the swapchain image size is not equal to the presentation size (e.g. because of DPI virtualization or display mode change emulation), MoltenVK's `vkQueuePresentKHR` returns `VK_SUBOPTIMAL_KHR` (see [`MVKSwapchain::hasOptimalSurface()`](https://github.com/KhronosGroup/MoltenVK/blob/a046e779df332f5c23a03df1cb6de4...). I can see this with a vkcube hacked to run in fullscreen, and it's especially problematic with wined3d which recreates the swapchain on `VK_SUBOPTIMAL_KHR` (every frame). Create the swapchain with `VkSwapchainPresentScalingCreateInfoEXT` when the window is being scaled to avoid this. I'm not sure if it's proper to be enabling the extension in winevulkan and then using it in win32u, but I don't see an obvious way to hook `vkCreateDevice` in win32u. -- v2: win32u: Create Vulkan swapchains with VkSwapchainPresentScalingCreateInfoEXT when the surface will be scaled. winevulkan: Enable VK_EXT_swapchain_maintenance1 when available. https://gitlab.winehq.org/wine/wine/-/merge_requests/8718
From: Brendan Shanks <bshanks(a)codeweavers.com> --- dlls/winevulkan/vulkan.c | 11 ++++++++++- include/wine/vulkan_driver.h | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 61b3a42d63f..ac93931092b 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -384,6 +384,8 @@ static VkResult wine_vk_physical_device_init(struct wine_phys_dev *object, VkPhy have_external_memory_host = TRUE; else if (!strcmp(host_properties[i].extensionName, "VK_EXT_map_memory_placed")) have_memory_placed = TRUE; + else if (!strcmp(host_properties[i].extensionName, "VK_EXT_swapchain_maintenance1")) + object->obj.has_swapchain_maintenance1 = true; else if (!strcmp(host_properties[i].extensionName, "VK_KHR_map_memory2")) have_map_memory2 = TRUE; } @@ -545,7 +547,7 @@ static VkResult wine_vk_device_convert_create_info(VkPhysicalDevice client_physi struct conversion_context *ctx, const VkDeviceCreateInfo *src, VkDeviceCreateInfo *dst) { struct wine_phys_dev *phys_dev = wine_phys_dev_from_handle(client_physical_device); - const char *extra_extensions[2], * const*extensions = src->ppEnabledExtensionNames; + const char *extra_extensions[3], * const*extensions = src->ppEnabledExtensionNames; unsigned int i, extra_count = 0, extensions_count = src->enabledExtensionCount; *dst = *src; @@ -590,6 +592,13 @@ static VkResult wine_vk_device_convert_create_info(VkPhysicalDevice client_physi extra_extensions[extra_count++] = "VK_EXT_external_memory_host"; } + /* win32u uses VkSwapchainPresentScalingCreateInfoEXT if available. */ + if (phys_dev->obj.has_swapchain_maintenance1) + { + if (!find_extension(extensions, extensions_count, "VK_EXT_swapchain_maintenance1")) + extra_extensions[extra_count++] = "VK_EXT_swapchain_maintenance1"; + } + if (extra_count) { const char **new_extensions; diff --git a/include/wine/vulkan_driver.h b/include/wine/vulkan_driver.h index 55613f3211f..0579ea7773f 100644 --- a/include/wine/vulkan_driver.h +++ b/include/wine/vulkan_driver.h @@ -21,6 +21,7 @@ #define __WINE_VULKAN_DRIVER_H #include <stdarg.h> +#include <stdbool.h> #include <stddef.h> #include <windef.h> @@ -90,6 +91,7 @@ struct vulkan_physical_device { VULKAN_OBJECT_HEADER( VkPhysicalDevice, physical_device ); struct vulkan_instance *instance; + bool has_swapchain_maintenance1; }; static inline struct vulkan_physical_device *vulkan_physical_device_from_handle( VkPhysicalDevice handle ) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/8718
From: Brendan Shanks <bshanks(a)codeweavers.com> --- dlls/win32u/vulkan.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/dlls/win32u/vulkan.c b/dlls/win32u/vulkan.c index 2e0c4627d71..722bdd845cf 100644 --- a/dlls/win32u/vulkan.c +++ b/dlls/win32u/vulkan.c @@ -269,9 +269,15 @@ static VkBool32 win32u_vkGetPhysicalDeviceWin32PresentationSupportKHR( VkPhysica return driver_funcs->p_vkGetPhysicalDeviceWin32PresentationSupportKHR( physical_device->host.physical_device, queue ); } +static BOOL extents_equals( const VkExtent2D *extents, const RECT *rect ) +{ + return extents->width == rect->right - rect->left && extents->height == rect->bottom - rect->top; +} + static VkResult win32u_vkCreateSwapchainKHR( VkDevice client_device, const VkSwapchainCreateInfoKHR *create_info, const VkAllocationCallbacks *allocator, VkSwapchainKHR *ret ) { + VkSwapchainPresentScalingCreateInfoEXT scaling = {.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_SCALING_CREATE_INFO_EXT}; struct swapchain *swapchain, *old_swapchain = swapchain_from_handle( create_info->oldSwapchain ); struct surface *surface = surface_from_handle( create_info->surface ); struct vulkan_device *device = vulkan_device_from_handle( client_device ); @@ -280,6 +286,7 @@ static VkResult win32u_vkCreateSwapchainKHR( VkDevice client_device, const VkSwa VkSwapchainCreateInfoKHR create_info_host = *create_info; VkSurfaceCapabilitiesKHR capabilities; VkSwapchainKHR host_swapchain; + RECT client_rect; VkResult res; if (!NtUserIsWindow( surface->hwnd )) @@ -298,6 +305,18 @@ static VkResult win32u_vkCreateSwapchainKHR( VkDevice client_device, const VkSwa create_info_host.imageExtent.width = max( create_info_host.imageExtent.width, capabilities.minImageExtent.width ); create_info_host.imageExtent.height = max( create_info_host.imageExtent.height, capabilities.minImageExtent.height ); + /* If the swapchain image size is not equal to the presentation size (e.g. because of DPI virtualization or + * display mode change emulation), MoltenVK's vkQueuePresentKHR returns VK_SUBOPTIMAL_KHR. + * Create the swapchain with VkSwapchainPresentScalingCreateInfoEXT to avoid this. + */ + if (NtUserGetClientRect( surface->hwnd, &client_rect, NtUserGetWinMonitorDpi( surface->hwnd, MDT_RAW_DPI ) ) && + !extents_equals( &create_info_host.imageExtent, &client_rect ) && + physical_device->has_swapchain_maintenance1) + { + scaling.scalingBehavior = VK_PRESENT_SCALING_STRETCH_BIT_EXT; + create_info_host.pNext = &scaling; + } + 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 ))) @@ -331,11 +350,6 @@ void win32u_vkDestroySwapchainKHR( VkDevice client_device, VkSwapchainKHR client free( swapchain ); } -static BOOL extents_equals( const VkExtent2D *extents, const RECT *rect ) -{ - return extents->width == rect->right - rect->left && extents->height == rect->bottom - rect->top; -} - static VkResult win32u_vkAcquireNextImage2KHR( VkDevice client_device, const VkAcquireNextImageInfoKHR *acquire_info, uint32_t *image_index ) { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/8718
On Thu Aug 7 22:58:30 2025 +0000, Brendan Shanks wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/8718/diffs?diff_id=199320&start_sha=c6c8d93418864258118170130c9d9add6951767b#05000c6e183209e030beb05f7baba884c43ccc3e_83_83) Yes that simplifies things, thanks!
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/8718#note_112414
This merge request was approved by Rémi Bernon. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/8718
participants (3)
-
Brendan Shanks -
Brendan Shanks (@bshanks) -
Rémi Bernon