GS/Vulkan: Release swap chain images on acquire fail

This commit is contained in:
Stenzek 2024-06-23 06:09:51 +10:00 committed by Connor McLaughlin
parent 57d225437e
commit 4924d8c984
5 changed files with 73 additions and 15 deletions

View File

@ -96,10 +96,11 @@ GSDeviceVK::GSDeviceVK()
GSDeviceVK::~GSDeviceVK() = default; GSDeviceVK::~GSDeviceVK() = default;
VkInstance GSDeviceVK::CreateVulkanInstance(const WindowInfo& wi, bool enable_debug_utils, bool enable_validation_layer) VkInstance GSDeviceVK::CreateVulkanInstance(const WindowInfo& wi, OptionalExtensions* oe, bool enable_debug_utils,
bool enable_validation_layer)
{ {
ExtensionList enabled_extensions; ExtensionList enabled_extensions;
if (!SelectInstanceExtensions(&enabled_extensions, wi, enable_debug_utils)) if (!SelectInstanceExtensions(&enabled_extensions, wi, oe, enable_debug_utils))
return VK_NULL_HANDLE; return VK_NULL_HANDLE;
// Remember to manually update this every release. We don't pull in svnrev.h here, because // Remember to manually update this every release. We don't pull in svnrev.h here, because
@ -143,7 +144,8 @@ VkInstance GSDeviceVK::CreateVulkanInstance(const WindowInfo& wi, bool enable_de
return instance; return instance;
} }
bool GSDeviceVK::SelectInstanceExtensions(ExtensionList* extension_list, const WindowInfo& wi, bool enable_debug_utils) bool GSDeviceVK::SelectInstanceExtensions(ExtensionList* extension_list, const WindowInfo& wi, OptionalExtensions* oe,
bool enable_debug_utils)
{ {
u32 extension_count = 0; u32 extension_count = 0;
VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr); VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr);
@ -204,6 +206,9 @@ bool GSDeviceVK::SelectInstanceExtensions(ExtensionList* extension_list, const W
if (enable_debug_utils && !SupportsExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, false)) if (enable_debug_utils && !SupportsExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, false))
Console.Warning("Vulkan: Debug report requested, but extension is not available."); Console.Warning("Vulkan: Debug report requested, but extension is not available.");
oe->vk_ext_swapchain_maintenance1 = (wi.type != WindowInfo::Type::Surfaceless &&
SupportsExtension(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME, false));
// Needed for exclusive fullscreen control. // Needed for exclusive fullscreen control.
SupportsExtension(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, false); SupportsExtension(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, false);
@ -276,7 +281,7 @@ GSDeviceVK::GPUList GSDeviceVK::EnumerateGPUs(VkInstance instance)
{ {
if (std::find_if(available_extension_list.begin(), available_extension_list.end(), [required_extension_name](const VkExtensionProperties& ext) { if (std::find_if(available_extension_list.begin(), available_extension_list.end(), [required_extension_name](const VkExtensionProperties& ext) {
return (std::strcmp(required_extension_name, ext.extensionName) == 0); return (std::strcmp(required_extension_name, ext.extensionName) == 0);
}) == available_extension_list.end()) }) == available_extension_list.end())
{ {
Console.Warning(fmt::format("Ignoring Vulkan GPU '{}' because is is missing required extension {}", Console.Warning(fmt::format("Ignoring Vulkan GPU '{}' because is is missing required extension {}",
props.deviceName, required_extension_name)); props.deviceName, required_extension_name));
@ -388,6 +393,10 @@ bool GSDeviceVK::SelectDeviceExtensions(ExtensionList* extension_list, bool enab
SupportsExtension(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME, false); SupportsExtension(VK_KHR_SHADER_NON_SEMANTIC_INFO_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);
#ifdef _WIN32 #ifdef _WIN32
m_optional_extensions.vk_ext_full_screen_exclusive = m_optional_extensions.vk_ext_full_screen_exclusive =
enable_surface && SupportsExtension(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME, false); enable_surface && SupportsExtension(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME, false);
@ -573,6 +582,8 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT}; VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT};
VkPhysicalDeviceLineRasterizationFeaturesEXT line_rasterization_feature = { VkPhysicalDeviceLineRasterizationFeaturesEXT line_rasterization_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT}; VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT};
VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT swapchain_maintenance1_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT};
if (m_optional_extensions.vk_ext_provoking_vertex) if (m_optional_extensions.vk_ext_provoking_vertex)
{ {
@ -589,6 +600,11 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
rasterization_order_access_feature.rasterizationOrderColorAttachmentAccess = VK_TRUE; rasterization_order_access_feature.rasterizationOrderColorAttachmentAccess = VK_TRUE;
Vulkan::AddPointerToChain(&device_info, &rasterization_order_access_feature); Vulkan::AddPointerToChain(&device_info, &rasterization_order_access_feature);
} }
if (m_optional_extensions.vk_ext_swapchain_maintenance1)
{
swapchain_maintenance1_feature.swapchainMaintenance1 = VK_TRUE;
Vulkan::AddPointerToChain(&device_info, &swapchain_maintenance1_feature);
}
VkResult res = vkCreateDevice(m_physical_device, &device_info, nullptr, &m_device); VkResult res = vkCreateDevice(m_physical_device, &device_info, nullptr, &m_device);
if (res != VK_SUCCESS) if (res != VK_SUCCESS)
@ -654,6 +670,8 @@ bool GSDeviceVK::ProcessDeviceExtensions()
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT}; VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT};
VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT rasterization_order_access_feature = { VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT rasterization_order_access_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT}; VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT};
VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT swapchain_maintenance1_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT, nullptr, VK_TRUE};
// add in optional feature structs // add in optional feature structs
if (m_optional_extensions.vk_ext_provoking_vertex) if (m_optional_extensions.vk_ext_provoking_vertex)
@ -662,6 +680,8 @@ bool GSDeviceVK::ProcessDeviceExtensions()
Vulkan::AddPointerToChain(&features2, &line_rasterization_feature); Vulkan::AddPointerToChain(&features2, &line_rasterization_feature);
if (m_optional_extensions.vk_ext_rasterization_order_attachment_access) if (m_optional_extensions.vk_ext_rasterization_order_attachment_access)
Vulkan::AddPointerToChain(&features2, &rasterization_order_access_feature); Vulkan::AddPointerToChain(&features2, &rasterization_order_access_feature);
if (m_optional_extensions.vk_ext_swapchain_maintenance1)
Vulkan::AddPointerToChain(&features2, &swapchain_maintenance1_feature);
// query // query
vkGetPhysicalDeviceFeatures2(m_physical_device, &features2); vkGetPhysicalDeviceFeatures2(m_physical_device, &features2);
@ -739,6 +759,9 @@ bool GSDeviceVK::ProcessDeviceExtensions()
m_optional_extensions.vk_ext_calibrated_timestamps = false; m_optional_extensions.vk_ext_calibrated_timestamps = false;
} }
m_optional_extensions.vk_ext_swapchain_maintenance1 &=
(swapchain_maintenance1_feature.swapchainMaintenance1 == VK_TRUE);
Console.WriteLn( Console.WriteLn(
"VK_EXT_provoking_vertex is %s", m_optional_extensions.vk_ext_provoking_vertex ? "supported" : "NOT supported"); "VK_EXT_provoking_vertex is %s", m_optional_extensions.vk_ext_provoking_vertex ? "supported" : "NOT supported");
Console.WriteLn( Console.WriteLn(
@ -747,6 +770,8 @@ bool GSDeviceVK::ProcessDeviceExtensions()
m_optional_extensions.vk_ext_calibrated_timestamps ? "supported" : "NOT supported"); m_optional_extensions.vk_ext_calibrated_timestamps ? "supported" : "NOT supported");
Console.WriteLn("VK_EXT_rasterization_order_attachment_access is %s", Console.WriteLn("VK_EXT_rasterization_order_attachment_access is %s",
m_optional_extensions.vk_ext_rasterization_order_attachment_access ? "supported" : "NOT supported"); m_optional_extensions.vk_ext_rasterization_order_attachment_access ? "supported" : "NOT supported");
Console.WriteLn("VK_EXT_swapchain_maintenance1 is %s",
m_optional_extensions.vk_ext_swapchain_maintenance1 ? "supported" : "NOT supported");
Console.WriteLn("VK_EXT_full_screen_exclusive is %s", Console.WriteLn("VK_EXT_full_screen_exclusive is %s",
m_optional_extensions.vk_ext_full_screen_exclusive ? "supported" : "NOT supported"); m_optional_extensions.vk_ext_full_screen_exclusive ? "supported" : "NOT supported");
Console.WriteLn("VK_KHR_driver_properties is %s", Console.WriteLn("VK_KHR_driver_properties is %s",
@ -1197,16 +1222,17 @@ void GSDeviceVK::SubmitCommandBuffer(VKSwapChain* present_swap_chain)
present_swap_chain->GetRenderingFinishedSemaphorePtr(), 1, present_swap_chain->GetSwapChainPtr(), present_swap_chain->GetRenderingFinishedSemaphorePtr(), 1, present_swap_chain->GetSwapChainPtr(),
present_swap_chain->GetCurrentImageIndexPtr(), nullptr}; present_swap_chain->GetCurrentImageIndexPtr(), nullptr};
present_swap_chain->ReleaseCurrentImage(); present_swap_chain->ResetImageAcquireResult();
const VkResult res = vkQueuePresentKHR(m_present_queue, &present_info); const VkResult res = vkQueuePresentKHR(m_present_queue, &present_info);
if (res != VK_SUCCESS) if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
{ {
// VK_ERROR_OUT_OF_DATE_KHR is not fatal, just means we need to recreate our swap chain. // VK_ERROR_OUT_OF_DATE_KHR is not fatal, just means we need to recreate our swap chain.
if (res != VK_ERROR_OUT_OF_DATE_KHR && res != VK_SUBOPTIMAL_KHR) if (res == VK_ERROR_OUT_OF_DATE_KHR)
ResizeWindow(0, 0, m_window_info.surface_scale);
else
LOG_VULKAN_ERROR(res, "vkQueuePresentKHR failed: "); LOG_VULKAN_ERROR(res, "vkQueuePresentKHR failed: ");
m_last_present_failed = true;
return; return;
} }
@ -1926,7 +1952,8 @@ void GSDeviceVK::GetAdaptersAndFullscreenModes(
{ {
if (Vulkan::LoadVulkanLibrary(nullptr)) if (Vulkan::LoadVulkanLibrary(nullptr))
{ {
const VkInstance instance = CreateVulkanInstance(WindowInfo(), false, false); OptionalExtensions oe = {};
const VkInstance instance = CreateVulkanInstance(WindowInfo(), &oe, false, false);
if (instance != VK_NULL_HANDLE) if (instance != VK_NULL_HANDLE)
{ {
if (Vulkan::LoadVulkanInstanceFunctions(instance)) if (Vulkan::LoadVulkanInstanceFunctions(instance))
@ -2136,7 +2163,7 @@ bool GSDeviceVK::UpdateWindow()
void GSDeviceVK::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) void GSDeviceVK::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
{ {
if (!m_swap_chain || (m_swap_chain->GetWidth() == static_cast<u32>(new_window_width) && if (!m_swap_chain || (m_swap_chain->GetWidth() == static_cast<u32>(new_window_width) &&
m_swap_chain->GetHeight() == static_cast<u32>(new_window_height))) m_swap_chain->GetHeight() == static_cast<u32>(new_window_height)))
{ {
// skip unnecessary resizes // skip unnecessary resizes
m_window_info.surface_scale = new_window_scale; m_window_info.surface_scale = new_window_scale;
@ -2413,7 +2440,7 @@ bool GSDeviceVK::CreateDeviceAndSwapChain()
if (!AcquireWindow(true)) if (!AcquireWindow(true))
return false; return false;
m_instance = CreateVulkanInstance(m_window_info, enable_debug_utils, enable_validation_layer); m_instance = CreateVulkanInstance(m_window_info, &m_optional_extensions, enable_debug_utils, enable_validation_layer);
if (m_instance == VK_NULL_HANDLE) if (m_instance == VK_NULL_HANDLE)
{ {
if (enable_debug_utils || enable_validation_layer) if (enable_debug_utils || enable_validation_layer)
@ -2421,7 +2448,7 @@ bool GSDeviceVK::CreateDeviceAndSwapChain()
// Try again without the validation layer. // Try again without the validation layer.
enable_debug_utils = false; enable_debug_utils = false;
enable_validation_layer = false; enable_validation_layer = false;
m_instance = CreateVulkanInstance(m_window_info, enable_debug_utils, enable_validation_layer); m_instance = CreateVulkanInstance(m_window_info, &m_optional_extensions, enable_debug_utils, enable_validation_layer);
if (m_instance == VK_NULL_HANDLE) if (m_instance == VK_NULL_HANDLE)
{ {
Host::ReportErrorAsync( Host::ReportErrorAsync(
@ -5714,7 +5741,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
BeginRenderPass(rp, render_area); BeginRenderPass(rp, render_area);
} }
} }
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne) if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne)
{ {
const VkClearAttachment ca = {VK_IMAGE_ASPECT_STENCIL_BIT, 0u, {.depthStencil = {0.0f, 1u}}}; const VkClearAttachment ca = {VK_IMAGE_ASPECT_STENCIL_BIT, 0u, {.depthStencil = {0.0f, 1u}}};

View File

@ -41,6 +41,7 @@ public:
bool vk_ext_rasterization_order_attachment_access : 1; bool vk_ext_rasterization_order_attachment_access : 1;
bool vk_ext_full_screen_exclusive : 1; bool vk_ext_full_screen_exclusive : 1;
bool vk_ext_line_rasterization : 1; bool vk_ext_line_rasterization : 1;
bool vk_ext_swapchain_maintenance1 : 1;
bool vk_khr_driver_properties : 1; bool vk_khr_driver_properties : 1;
bool vk_khr_shader_non_semantic_info : 1; bool vk_khr_shader_non_semantic_info : 1;
}; };
@ -125,7 +126,8 @@ public:
private: private:
// Helper method to create a Vulkan instance. // Helper method to create a Vulkan instance.
static VkInstance CreateVulkanInstance(const WindowInfo& wi, bool enable_debug_utils, bool enable_validation_layer); static VkInstance CreateVulkanInstance(const WindowInfo& wi, OptionalExtensions* oe, bool enable_debug_utils,
bool enable_validation_layer);
// Returns a list of Vulkan-compatible GPUs. // Returns a list of Vulkan-compatible GPUs.
using GPUList = std::vector<std::pair<VkPhysicalDevice, std::string>>; using GPUList = std::vector<std::pair<VkPhysicalDevice, std::string>>;
@ -173,7 +175,8 @@ private:
}; };
using ExtensionList = std::vector<const char*>; using ExtensionList = std::vector<const char*>;
static bool SelectInstanceExtensions(ExtensionList* extension_list, const WindowInfo& wi, bool enable_debug_utils); static bool SelectInstanceExtensions(ExtensionList* extension_list, const WindowInfo& wi, OptionalExtensions* oe,
bool enable_debug_utils);
bool SelectDeviceExtensions(ExtensionList* extension_list, bool enable_surface); bool SelectDeviceExtensions(ExtensionList* extension_list, bool enable_surface);
bool SelectDeviceFeatures(); bool SelectDeviceFeatures();
bool CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer); bool CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer);

View File

@ -238,4 +238,7 @@ VULKAN_DEVICE_ENTRY_POINT(vkGetCalibratedTimestampsEXT, false)
// VK_KHR_push_descriptor // VK_KHR_push_descriptor
VULKAN_DEVICE_ENTRY_POINT(vkCmdPushDescriptorSetKHR, false) VULKAN_DEVICE_ENTRY_POINT(vkCmdPushDescriptorSetKHR, false)
// VK_EXT_swapchain_maintenance1
VULKAN_DEVICE_ENTRY_POINT(vkReleaseSwapchainImagesEXT, false)
#endif // VULKAN_DEVICE_ENTRY_POINT #endif // VULKAN_DEVICE_ENTRY_POINT

View File

@ -544,12 +544,35 @@ VkResult VKSwapChain::AcquireNextImage()
} }
void VKSwapChain::ReleaseCurrentImage() void VKSwapChain::ReleaseCurrentImage()
{
if (!m_image_acquire_result.has_value())
return;
if ((m_image_acquire_result.value() == VK_SUCCESS || m_image_acquire_result.value() == VK_SUBOPTIMAL_KHR) &&
GSDeviceVK::GetInstance()->GetOptionalExtensions().vk_ext_swapchain_maintenance1)
{
GSDeviceVK::GetInstance()->WaitForGPUIdle();
const VkReleaseSwapchainImagesInfoEXT info = {.sType = VK_STRUCTURE_TYPE_RELEASE_SWAPCHAIN_IMAGES_INFO_EXT,
.swapchain = m_swap_chain,
.imageIndexCount = 1,
.pImageIndices = &m_current_image};
VkResult res = vkReleaseSwapchainImagesEXT(GSDeviceVK::GetInstance()->GetDevice(), &info);
if (res != VK_SUCCESS)
LOG_VULKAN_ERROR(res, "vkReleaseSwapchainImagesEXT() failed: ");
}
m_image_acquire_result.reset();
}
void VKSwapChain::ResetImageAcquireResult()
{ {
m_image_acquire_result.reset(); m_image_acquire_result.reset();
} }
bool VKSwapChain::ResizeSwapChain(u32 new_width, u32 new_height, float new_scale) bool VKSwapChain::ResizeSwapChain(u32 new_width, u32 new_height, float new_scale)
{ {
ReleaseCurrentImage();
DestroySwapChainImages(); DestroySwapChainImages();
if (new_width != 0 && new_height != 0) if (new_width != 0 && new_height != 0)
@ -578,6 +601,7 @@ bool VKSwapChain::SetPresentMode(VkPresentModeKHR present_mode)
// Recreate the swap chain with the new present mode. // Recreate the swap chain with the new present mode.
INFO_LOG("Recreating swap chain to change present mode."); INFO_LOG("Recreating swap chain to change present mode.");
ReleaseCurrentImage();
DestroySwapChainImages(); DestroySwapChainImages();
if (!CreateSwapChain()) if (!CreateSwapChain())
{ {

View File

@ -68,6 +68,7 @@ public:
VkFormat GetTextureFormat() const; VkFormat GetTextureFormat() const;
VkResult AcquireNextImage(); VkResult AcquireNextImage();
void ReleaseCurrentImage(); void ReleaseCurrentImage();
void ResetImageAcquireResult();
bool RecreateSurface(const WindowInfo& new_wi); bool RecreateSurface(const WindowInfo& new_wi);
bool ResizeSwapChain(u32 new_width = 0, u32 new_height = 0, float new_scale = 1.0f); bool ResizeSwapChain(u32 new_width = 0, u32 new_height = 0, float new_scale = 1.0f);