[PATCH 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. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/8718
From: Brendan Shanks <bshanks(a)codeweavers.com> --- dlls/winevulkan/vulkan.c | 11 ++++++++++- dlls/winevulkan/vulkan_private.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 61b3a42d63f..d4b9dd9ba02 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->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->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/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h index 59b900930b3..db0a72302f3 100644 --- a/dlls/winevulkan/vulkan_private.h +++ b/dlls/winevulkan/vulkan_private.h @@ -80,6 +80,7 @@ struct wine_phys_dev uint32_t external_memory_align; uint32_t map_placed_align; + uint32_t swapchain_maintenance1; }; struct wine_debug_report_callback; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/8718
From: Brendan Shanks <bshanks(a)codeweavers.com> --- dlls/win32u/vulkan.c | 54 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/dlls/win32u/vulkan.c b/dlls/win32u/vulkan.c index 2e0c4627d71..868cd638078 100644 --- a/dlls/win32u/vulkan.c +++ b/dlls/win32u/vulkan.c @@ -269,9 +269,45 @@ 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 BOOL has_swapchain_maintenance1( struct vulkan_instance *instance, VkPhysicalDevice physical_device ) +{ + VkExtensionProperties *properties = NULL; + uint32_t num_properties; + BOOL ret = FALSE; + unsigned int i; + VkResult res; + + res = instance->p_vkEnumerateDeviceExtensionProperties( physical_device, NULL, &num_properties, NULL); + if (res != VK_SUCCESS) return FALSE; + + properties = calloc(num_properties, sizeof(*properties)); + if (!properties) return FALSE; + + res = instance->p_vkEnumerateDeviceExtensionProperties( physical_device, NULL, &num_properties, properties); + if (res != VK_SUCCESS) num_properties = 0; + + for (i = 0; i < num_properties; i++) + { + if (!strcmp(properties[i].extensionName, "VK_EXT_swapchain_maintenance1")) + { + ret = TRUE; + break; + } + } + + free(properties); + return ret; +} + 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 +316,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 +335,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 ) && + has_swapchain_maintenance1( instance, physical_device->host.physical_device )) + { + 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 +380,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
Rémi Bernon (@rbernon) commented about dlls/winevulkan/vulkan_private.h:
uint32_t external_memory_align; uint32_t map_placed_align; + uint32_t swapchain_maintenance1;
This could probably be moved to `struct vulkan_physical_device` which is shared with win32u, so we then wouldn't need `has_swapchain_maintenance1`. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/8718#note_112311
participants (3)
-
Brendan Shanks -
Brendan Shanks (@bshanks) -
Rémi Bernon