Merge pull request #6945 from Themaister/master

Vulkan: WSI stability fixes
This commit is contained in:
Twinaphex 2018-07-04 21:04:08 +02:00 committed by GitHub
commit 63a071b85a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -43,6 +43,29 @@ static VkInstance cached_instance_vk;
static VkDevice cached_device_vk; static VkDevice cached_device_vk;
static retro_vulkan_destroy_device_t cached_destroy_device_vk; static retro_vulkan_destroy_device_t cached_destroy_device_vk;
//#define WSI_HARDENING_TEST
#ifdef WSI_HARDENING_TEST
static unsigned wsi_harden_counter = 0;
static unsigned wsi_harden_counter2 = 0;
static void trigger_spurious_error_vkresult(VkResult *res)
{
++wsi_harden_counter;
if ((wsi_harden_counter & 15) == 12)
*res = VK_ERROR_OUT_OF_DATE_KHR;
else if ((wsi_harden_counter & 31) == 13)
*res = VK_ERROR_OUT_OF_DATE_KHR;
else if ((wsi_harden_counter & 15) == 6)
*res = VK_ERROR_SURFACE_LOST_KHR;
}
static bool trigger_spurious_error(void)
{
++wsi_harden_counter2;
return ((wsi_harden_counter2 & 15) == 9) || ((wsi_harden_counter2 & 15) == 10);
}
#endif
#ifdef VULKAN_DEBUG #ifdef VULKAN_DEBUG
static VKAPI_ATTR VkBool32 VKAPI_CALL vulkan_debug_cb( static VKAPI_ATTR VkBool32 VKAPI_CALL vulkan_debug_cb(
VkDebugReportFlagsEXT flags, VkDebugReportFlagsEXT flags,
@ -2278,6 +2301,32 @@ bool vulkan_surface_create(gfx_ctx_vulkan_data_t *vk,
return true; return true;
} }
static void vulkan_destroy_swapchain(gfx_ctx_vulkan_data_t *vk)
{
unsigned i;
if (vk->swapchain != VK_NULL_HANDLE)
{
vkDeviceWaitIdle(vk->context.device);
vkDestroySwapchainKHR(vk->context.device, vk->swapchain, NULL);
memset(vk->context.swapchain_images, 0, sizeof(vk->context.swapchain_images));
vk->swapchain = VK_NULL_HANDLE;
}
for (i = 0; i < VULKAN_MAX_SWAPCHAIN_IMAGES; i++)
{
if (vk->context.swapchain_semaphores[i] != VK_NULL_HANDLE)
vkDestroySemaphore(vk->context.device,
vk->context.swapchain_semaphores[i], NULL);
if (vk->context.swapchain_fences[i] != VK_NULL_HANDLE)
vkDestroyFence(vk->context.device,
vk->context.swapchain_fences[i], NULL);
}
memset(vk->context.swapchain_semaphores, 0, sizeof(vk->context.swapchain_semaphores));
memset(vk->context.swapchain_fences, 0, sizeof(vk->context.swapchain_fences));
}
void vulkan_present(gfx_ctx_vulkan_data_t *vk, unsigned index) void vulkan_present(gfx_ctx_vulkan_data_t *vk, unsigned index)
{ {
VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
@ -2304,10 +2353,14 @@ void vulkan_present(gfx_ctx_vulkan_data_t *vk, unsigned index)
#endif #endif
err = vkQueuePresentKHR(vk->context.queue, &present); err = vkQueuePresentKHR(vk->context.queue, &present);
#ifdef WSI_HARDENING_TEST
trigger_spurious_error_vkresult(&err);
#endif
if (err != VK_SUCCESS || result != VK_SUCCESS) if (err != VK_SUCCESS || result != VK_SUCCESS)
{ {
RARCH_LOG("[Vulkan]: QueuePresent failed, invalidating swapchain.\n"); RARCH_LOG("[Vulkan]: QueuePresent failed, destroying swapchain.\n");
vk->context.invalid_swapchain = true; vulkan_destroy_swapchain(vk);
} }
#ifdef HAVE_THREADS #ifdef HAVE_THREADS
@ -2325,12 +2378,8 @@ void vulkan_context_destroy(gfx_ctx_vulkan_data_t *vk,
if (vk->context.device) if (vk->context.device)
vkDeviceWaitIdle(vk->context.device); vkDeviceWaitIdle(vk->context.device);
if (vk->swapchain)
{ vulkan_destroy_swapchain(vk);
vkDestroySwapchainKHR(vk->context.device,
vk->swapchain, NULL);
vk->swapchain = VK_NULL_HANDLE;
}
if (destroy_surface && vk->vk_surface != VK_NULL_HANDLE) if (destroy_surface && vk->vk_surface != VK_NULL_HANDLE)
{ {
@ -2339,16 +2388,6 @@ void vulkan_context_destroy(gfx_ctx_vulkan_data_t *vk,
vk->vk_surface = VK_NULL_HANDLE; vk->vk_surface = VK_NULL_HANDLE;
} }
for (i = 0; i < VULKAN_MAX_SWAPCHAIN_IMAGES; i++)
{
if (vk->context.swapchain_semaphores[i] != VK_NULL_HANDLE)
vkDestroySemaphore(vk->context.device,
vk->context.swapchain_semaphores[i], NULL);
if (vk->context.swapchain_fences[i] != VK_NULL_HANDLE)
vkDestroyFence(vk->context.device,
vk->context.swapchain_fences[i], NULL);
}
#ifdef VULKAN_DEBUG #ifdef VULKAN_DEBUG
if (vk->context.debug_callback) if (vk->context.debug_callback)
vkDestroyDebugReportCallbackEXT(vk->context.instance, vk->context.debug_callback, NULL); vkDestroyDebugReportCallbackEXT(vk->context.instance, vk->context.debug_callback, NULL);
@ -2423,8 +2462,10 @@ void vulkan_acquire_next_image(gfx_ctx_vulkan_data_t *vk)
{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
VkSemaphoreCreateInfo sem_info = VkSemaphoreCreateInfo sem_info =
{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
bool is_retrying = false; bool is_retrying = false;
retry:
if (vk->swapchain == VK_NULL_HANDLE) if (vk->swapchain == VK_NULL_HANDLE)
{ {
/* We don't have a swapchain, try to create one now. */ /* We don't have a swapchain, try to create one now. */
@ -2441,52 +2482,63 @@ void vulkan_acquire_next_image(gfx_ctx_vulkan_data_t *vk)
vk->context.current_swapchain_index = 0; vk->context.current_swapchain_index = 0;
vulkan_acquire_clear_fences(vk); vulkan_acquire_clear_fences(vk);
vulkan_acquire_wait_fences(vk); vulkan_acquire_wait_fences(vk);
vk->context.invalid_swapchain = true;
return; return;
} }
} }
retry:
vkCreateFence(vk->context.device, &fence_info, NULL, &fence); vkCreateFence(vk->context.device, &fence_info, NULL, &fence);
err = vkAcquireNextImageKHR(vk->context.device, err = vkAcquireNextImageKHR(vk->context.device,
vk->swapchain, UINT64_MAX, vk->swapchain, UINT64_MAX,
VK_NULL_HANDLE, fence, &vk->context.current_swapchain_index); VK_NULL_HANDLE, fence, &vk->context.current_swapchain_index);
index = vk->context.current_swapchain_index;
if (vk->context.swapchain_semaphores[index] == VK_NULL_HANDLE)
vkCreateSemaphore(vk->context.device, &sem_info,
NULL, &vk->context.swapchain_semaphores[index]);
if (err == VK_SUCCESS) if (err == VK_SUCCESS)
vkWaitForFences(vk->context.device, 1, &fence, true, UINT64_MAX); vkWaitForFences(vk->context.device, 1, &fence, true, UINT64_MAX);
#ifdef WSI_HARDENING_TEST
trigger_spurious_error_vkresult(&err);
#endif
vkDestroyFence(vk->context.device, fence, NULL); vkDestroyFence(vk->context.device, fence, NULL);
vulkan_acquire_wait_fences(vk); if (err == VK_ERROR_OUT_OF_DATE_KHR)
if (err != VK_SUCCESS)
{ {
/* Throw away the old swapchain and try again. */
vulkan_destroy_swapchain(vk);
if (is_retrying) if (is_retrying)
{ {
RARCH_ERR("[Vulkan]: Tried acquring next swapchain image after creating new one, but failed ...\n"); RARCH_ERR("[Vulkan]: Swapchain is out of date, trying to create new one. Have tried multiple times ...\n");
retro_sleep(10);
} }
else else
{ RARCH_ERR("[Vulkan]: Swapchain is out of date, trying to create new one.\n");
RARCH_LOG("[Vulkan]: AcquireNextImage failed, invalidating swapchain.\n");
vk->context.invalid_swapchain = true;
RARCH_LOG("[Vulkan]: AcquireNextImage failed, so trying to recreate swapchain.\n");
if (!vulkan_create_swapchain(vk, vk->context.swapchain_width,
vk->context.swapchain_height, vk->context.swap_interval))
{
RARCH_ERR("[Vulkan]: Failed to create new swapchain.\n");
}
else
{
is_retrying = true; is_retrying = true;
vulkan_acquire_clear_fences(vk);
goto retry; goto retry;
} }
else if (err != VK_SUCCESS)
{
/* We are screwed, don't try anymore. Maybe it will work later. */
vulkan_destroy_swapchain(vk);
RARCH_ERR("[Vulkan]: Failed to acquire from swapchain (err = %d).\n",
(int)err);
if (err == VK_ERROR_SURFACE_LOST_KHR)
RARCH_ERR("[Vulkan]: Got VK_ERROR_SURFACE_LOST_KHR.\n");
/* Force driver to reset swapchain image handles. */
vk->context.invalid_swapchain = true;
vulkan_acquire_clear_fences(vk);
return;
} }
index = vk->context.current_swapchain_index;
if (vk->context.swapchain_semaphores[index] == VK_NULL_HANDLE)
{
vkCreateSemaphore(vk->context.device, &sem_info,
NULL, &vk->context.swapchain_semaphores[index]);
} }
vulkan_acquire_wait_fences(vk);
} }
bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk, bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
@ -2511,6 +2563,7 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
VkCompositeAlphaFlagBitsKHR composite = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; VkCompositeAlphaFlagBitsKHR composite = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
vkDeviceWaitIdle(vk->context.device); vkDeviceWaitIdle(vk->context.device);
vulkan_acquire_clear_fences(vk);
vk->created_new_swapchain = true; vk->created_new_swapchain = true;
if (vk->swapchain != VK_NULL_HANDLE && if (vk->swapchain != VK_NULL_HANDLE &&
@ -2613,10 +2666,8 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
else else
swapchain_size = surface_properties.currentExtent; swapchain_size = surface_properties.currentExtent;
#if 0 #ifdef WSI_HARDENING_TEST
/* Tests for deferred creation. */ if (trigger_spurious_error())
static unsigned retry_count = 0;
if (++retry_count < 50)
{ {
surface_properties.maxImageExtent.width = 0; surface_properties.maxImageExtent.width = 0;
surface_properties.maxImageExtent.height = 0; surface_properties.maxImageExtent.height = 0;
@ -2657,10 +2708,6 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
* We hard sync against the swapchain, so if we have 2 images, * We hard sync against the swapchain, so if we have 2 images,
* we would be unable to overlap CPU and GPU, which can get very slow * we would be unable to overlap CPU and GPU, which can get very slow
* for GPU-rendered cores. */ * for GPU-rendered cores. */
desired_swapchain_images = 3;
/* Limit latency. */
if (desired_swapchain_images > settings->uints.video_max_swapchain_images)
desired_swapchain_images = settings->uints.video_max_swapchain_images; desired_swapchain_images = settings->uints.video_max_swapchain_images;
/* Clamp images requested to what is supported by the implementation. */ /* Clamp images requested to what is supported by the implementation. */
@ -2763,8 +2810,7 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
RARCH_LOG("[Vulkan]: Got %u swapchain images.\n", RARCH_LOG("[Vulkan]: Got %u swapchain images.\n",
vk->context.num_swapchain_images); vk->context.num_swapchain_images);
vulkan_acquire_clear_fences(vk); /* Force driver to reset swapchain image handles. */
vk->context.invalid_swapchain = true; vk->context.invalid_swapchain = true;
return true; return true;
} }