From: Brendan Shanks bshanks@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 ) {