VulkanDevice: Use VK_EXT_surface_maintenance1

NVIDIA drivers return a minImageCount of 3 with the base surface query
capability, but require 4 images for XWayland in immediate present mode.
This commit is contained in:
Stenzek 2024-11-10 16:05:45 +10:00
parent b70a8482bd
commit 0234137be4
No known key found for this signature in database
3 changed files with 51 additions and 20 deletions

View File

@ -272,8 +272,12 @@ bool VulkanDevice::SelectInstanceExtensions(ExtensionList* extension_list, const
// Needed for exclusive fullscreen control.
SupportsExtension(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, false);
oe->vk_ext_swapchain_maintenance1 =
oe->vk_khr_get_surface_capabilities2 = (wi.type != WindowInfo::Type::Surfaceless &&
SupportsExtension(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, false));
oe->vk_ext_surface_maintenance1 =
(wi.type != WindowInfo::Type::Surfaceless && SupportsExtension(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME, false));
oe->vk_ext_swapchain_maintenance1 = (wi.type != WindowInfo::Type::Surfaceless &&
SupportsExtension(VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME, false));
oe->vk_khr_get_physical_device_properties2 =
SupportsExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, false);
@ -468,8 +472,7 @@ bool VulkanDevice::SelectDeviceExtensions(ExtensionList* extension_list, bool en
m_optional_extensions.vk_ext_external_memory_host =
SupportsExtension(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME, false);
m_optional_extensions.vk_ext_swapchain_maintenance1 =
m_optional_extensions.vk_ext_swapchain_maintenance1 &&
SupportsExtension(VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME, false);
enable_surface && SupportsExtension(VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME, false);
// Dynamic rendering isn't strictly needed for FSI, but we want it with framebufferless rendering.
m_optional_extensions.vk_ext_fragment_shader_interlock =
@ -797,16 +800,19 @@ void VulkanDevice::ProcessDeviceExtensions()
#define LOG_EXT(name, field) INFO_LOG(name " is {}", m_optional_extensions.field ? "supported" : "NOT supported")
LOG_EXT("VK_EXT_external_memory_host", vk_ext_external_memory_host);
LOG_EXT("VK_EXT_memory_budget", vk_ext_memory_budget);
LOG_EXT("VK_EXT_fragment_shader_interlock", vk_ext_fragment_shader_interlock);
LOG_EXT("VK_EXT_memory_budget", vk_ext_memory_budget);
LOG_EXT("VK_EXT_rasterization_order_attachment_access", vk_ext_rasterization_order_attachment_access);
LOG_EXT("VK_EXT_surface_maintenance1", vk_ext_surface_maintenance1);
LOG_EXT("VK_EXT_swapchain_maintenance1", vk_ext_swapchain_maintenance1);
LOG_EXT("VK_KHR_get_memory_requirements2", vk_khr_get_memory_requirements2);
LOG_EXT("VK_KHR_bind_memory2", vk_khr_bind_memory2);
LOG_EXT("VK_KHR_get_physical_device_properties2", vk_khr_get_physical_device_properties2);
LOG_EXT("VK_KHR_dedicated_allocation", vk_khr_dedicated_allocation);
LOG_EXT("VK_KHR_driver_properties", vk_khr_driver_properties);
LOG_EXT("VK_KHR_dynamic_rendering", vk_khr_dynamic_rendering);
LOG_EXT("VK_KHR_dynamic_rendering_local_read", vk_khr_dynamic_rendering_local_read);
LOG_EXT("VK_KHR_get_surface_capabilities2", vk_khr_get_surface_capabilities2);
LOG_EXT("VK_KHR_maintenance4", vk_khr_maintenance4);
LOG_EXT("VK_KHR_maintenance5", vk_khr_maintenance5);
LOG_EXT("VK_KHR_push_descriptor", vk_khr_push_descriptor);

View File

@ -47,6 +47,7 @@ public:
bool vk_ext_full_screen_exclusive : 1;
bool vk_ext_memory_budget : 1;
bool vk_ext_rasterization_order_attachment_access : 1;
bool vk_ext_surface_maintenance1 : 1;
bool vk_ext_swapchain_maintenance1 : 1;
bool vk_khr_get_memory_requirements2 : 1;
bool vk_khr_bind_memory2 : 1;
@ -55,6 +56,7 @@ public:
bool vk_khr_driver_properties : 1;
bool vk_khr_dynamic_rendering : 1;
bool vk_khr_dynamic_rendering_local_read : 1;
bool vk_khr_get_surface_capabilities2 : 1;
bool vk_khr_maintenance4 : 1;
bool vk_khr_maintenance5 : 1;
bool vk_khr_push_descriptor : 1;

View File

@ -321,25 +321,48 @@ bool VulkanSwapChain::CreateSwapChain(VulkanDevice& dev, Error* error)
return false;
// Look up surface properties to determine image count and dimensions
VkSurfaceCapabilitiesKHR surface_capabilities;
VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physdev, m_surface, &surface_capabilities);
VkSurfaceCapabilities2KHR surface_caps = {
.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR, .pNext = nullptr, .surfaceCapabilities = {}};
VkResult res = VK_NOT_READY;
// The present mode can alter the number of images required. Use VK_KHR_get_surface_capabilities2 to confirm it.
if (dev.GetOptionalExtensions().vk_khr_get_surface_capabilities2 &&
dev.GetOptionalExtensions().vk_ext_surface_maintenance1)
{
VkPhysicalDeviceSurfaceInfo2KHR dsi = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR, .pNext = nullptr, .surface = m_surface};
VkSurfacePresentModeEXT dsi_pm = {
.sType = VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT, .pNext = nullptr, .presentMode = present_mode.value()};
Vulkan::AddPointerToChain(&dsi, &dsi_pm);
res = vkGetPhysicalDeviceSurfaceCapabilities2KHR(physdev, &dsi, &surface_caps);
if (res != VK_SUCCESS)
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceCapabilities2KHR() failed: ");
}
if (res != VK_SUCCESS)
{
DEV_LOG("VK_EXT_surface_maintenance1 not supported, image count may be sub-optimal.");
res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physdev, m_surface, &surface_caps.surfaceCapabilities);
if (res != VK_SUCCESS)
{
Vulkan::SetErrorObject(error, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed: ", res);
return false;
}
}
// Select number of images in swap chain, we prefer one buffer in the background to work on in triple-buffered mode.
// maxImageCount can be zero, in which case there isn't an upper limit on the number of buffers.
u32 image_count = std::clamp<u32>(
(present_mode.value() == VK_PRESENT_MODE_MAILBOX_KHR) ? 3 : 2, surface_capabilities.minImageCount,
(surface_capabilities.maxImageCount == 0) ? std::numeric_limits<u32>::max() : surface_capabilities.maxImageCount);
(present_mode.value() == VK_PRESENT_MODE_MAILBOX_KHR) ? 3 : 2, surface_caps.surfaceCapabilities.minImageCount,
(surface_caps.surfaceCapabilities.maxImageCount == 0) ? std::numeric_limits<u32>::max() :
surface_caps.surfaceCapabilities.maxImageCount);
DEV_LOG("Creating a swap chain with {} images in present mode {}", image_count,
PresentModeToString(present_mode.value()));
// Determine the dimensions of the swap chain. Values of -1 indicate the size we specify here
// determines window size? Android sometimes lags updating currentExtent, so don't use it.
VkExtent2D size = surface_capabilities.currentExtent;
VkExtent2D size = surface_caps.surfaceCapabilities.currentExtent;
#ifndef __ANDROID__
if (size.width == UINT32_MAX)
#endif
@ -347,27 +370,27 @@ bool VulkanSwapChain::CreateSwapChain(VulkanDevice& dev, Error* error)
size.width = m_window_info.surface_width;
size.height = m_window_info.surface_height;
}
size.width =
std::clamp(size.width, surface_capabilities.minImageExtent.width, surface_capabilities.maxImageExtent.width);
size.height =
std::clamp(size.height, surface_capabilities.minImageExtent.height, surface_capabilities.maxImageExtent.height);
size.width = std::clamp(size.width, surface_caps.surfaceCapabilities.minImageExtent.width,
surface_caps.surfaceCapabilities.maxImageExtent.width);
size.height = std::clamp(size.height, surface_caps.surfaceCapabilities.minImageExtent.height,
surface_caps.surfaceCapabilities.maxImageExtent.height);
// Prefer identity transform if possible
VkSurfaceTransformFlagBitsKHR transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
if (!(surface_capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR))
transform = surface_capabilities.currentTransform;
if (!(surface_caps.surfaceCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR))
transform = surface_caps.surfaceCapabilities.currentTransform;
VkCompositeAlphaFlagBitsKHR alpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
if (!(surface_capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR))
if (!(surface_caps.surfaceCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR))
{
// If we only support pre-multiplied/post-multiplied... :/
if (surface_capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
if (surface_caps.surfaceCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
alpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
}
// Select swap chain flags, we only need a colour attachment
VkImageUsageFlags image_usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
if ((surface_capabilities.supportedUsageFlags & image_usage) != image_usage)
if ((surface_caps.surfaceCapabilities.supportedUsageFlags & image_usage) != image_usage)
{
Error::SetStringView(error, "Swap chain does not support usage as color attachment");
return false;