diff --git a/gfx/common/vulkan_common.c b/gfx/common/vulkan_common.c index 6dff280ce9..cca8008b5c 100644 --- a/gfx/common/vulkan_common.c +++ b/gfx/common/vulkan_common.c @@ -38,6 +38,12 @@ #include "../../libretro-common/include/retro_math.h" #include "../../libretro-common/include/string/stdstring.h" +// Windows is not particularly good at recreating swapchains. +// Emulate vsync toggling by using vkAcquireNextImageKHR timeouts. +#if defined(_WIN32) +#define VULKAN_EMULATE_MAILBOX +#endif + static dylib_t vulkan_library; static VkInstance cached_instance_vk; static VkDevice cached_device_vk; @@ -1494,6 +1500,10 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk) "VK_KHR_sampler_mirror_clamp_to_edge", }; +#ifdef VULKAN_EMULATE_MAILBOX + vk->emulate_mailbox = true; +#endif + #ifdef VULKAN_DEBUG static const char *device_layers[] = { "VK_LAYER_LUNARG_standard_validation" }; #endif @@ -2333,6 +2343,7 @@ static void vulkan_destroy_swapchain(gfx_ctx_vulkan_data_t *vk) vkDestroySwapchainKHR(vk->context.device, vk->swapchain, NULL); memset(vk->context.swapchain_images, 0, sizeof(vk->context.swapchain_images)); vk->swapchain = VK_NULL_HANDLE; + vk->context.has_acquired_swapchain = false; } for (i = 0; i < VULKAN_MAX_SWAPCHAIN_IMAGES; i++) @@ -2351,9 +2362,13 @@ static void vulkan_destroy_swapchain(gfx_ctx_vulkan_data_t *vk) void vulkan_present(gfx_ctx_vulkan_data_t *vk, unsigned index) { - VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; - VkResult result = VK_SUCCESS; - VkResult err = VK_SUCCESS; + VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; + VkResult result = VK_SUCCESS; + VkResult err = VK_SUCCESS; + + if (!vk->context.has_acquired_swapchain) + return; + vk->context.has_acquired_swapchain = false; /* We're still waiting for a proper swapchain, so just fake it. */ if (vk->swapchain == VK_NULL_HANDLE) @@ -2525,12 +2540,29 @@ retry: vkCreateFence(vk->context.device, &fence_info, NULL, &fence); - err = vkAcquireNextImageKHR(vk->context.device, - vk->swapchain, UINT64_MAX, - VK_NULL_HANDLE, fence, &vk->context.current_swapchain_index); + if (vk->emulating_mailbox) + { + /* Non-blocking acquire. If we don't get a swapchain frame right away, + * just skip rendering to the swapchain this frame, similar to what + * MAILBOX would do. */ + err = vkAcquireNextImageKHR(vk->context.device, + vk->swapchain, 0, + VK_NULL_HANDLE, fence, &vk->context.current_swapchain_index); + } + else + { + err = vkAcquireNextImageKHR(vk->context.device, + vk->swapchain, UINT64_MAX, + VK_NULL_HANDLE, fence, &vk->context.current_swapchain_index); + } if (err == VK_SUCCESS) + { vkWaitForFences(vk->context.device, 1, &fence, true, UINT64_MAX); + vk->context.has_acquired_swapchain = true; + } + else + vk->context.has_acquired_swapchain = false; #ifdef WSI_HARDENING_TEST trigger_spurious_error_vkresult(&err); @@ -2538,7 +2570,13 @@ retry: vkDestroyFence(vk->context.device, fence, NULL); - if (err == VK_ERROR_OUT_OF_DATE_KHR) + if (err == VK_NOT_READY || err == VK_TIMEOUT) + { + /* Just pretend we have a swapchain index, round-robin style. */ + vk->context.current_swapchain_index = + (vk->context.current_swapchain_index + 1) % vk->context.num_swapchain_images; + } + else if (err == VK_ERROR_OUT_OF_DATE_KHR) { /* Throw away the old swapchain and try again. */ vulkan_destroy_swapchain(vk); @@ -2601,6 +2639,14 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk, vkDeviceWaitIdle(vk->context.device); vulkan_acquire_clear_fences(vk); + if (swap_interval == 0 && vk->emulate_mailbox) + { + swap_interval = 1; + vk->emulating_mailbox = true; + } + else + vk->emulating_mailbox = false; + vk->created_new_swapchain = true; if (vk->swapchain != VK_NULL_HANDLE && !vk->context.invalid_swapchain && diff --git a/gfx/common/vulkan_common.h b/gfx/common/vulkan_common.h index db94a08b63..8fe426776f 100644 --- a/gfx/common/vulkan_common.h +++ b/gfx/common/vulkan_common.h @@ -96,6 +96,7 @@ typedef struct vulkan_context /* Used by screenshot to get blits with correct colorspace. */ bool swapchain_is_srgb; bool swap_interval_emulation_lock; + bool has_acquired_swapchain; unsigned swapchain_width; unsigned swapchain_height; @@ -131,6 +132,8 @@ typedef struct gfx_ctx_vulkan_data { bool need_new_swapchain; bool created_new_swapchain; + bool emulate_mailbox; + bool emulating_mailbox; vulkan_context_t context; VkSurfaceKHR vk_surface; VkSwapchainKHR swapchain; diff --git a/gfx/drivers/vulkan.c b/gfx/drivers/vulkan.c index c83254eadd..86cb84d982 100644 --- a/gfx/drivers/vulkan.c +++ b/gfx/drivers/vulkan.c @@ -1778,7 +1778,7 @@ static bool vulkan_frame(void *data, const void *frame, (vulkan_filter_chain_t*)vk->filter_chain, vk->cmd, &vk->vk_vp); /* Render to backbuffer. */ - if (chain->backbuffer.image != VK_NULL_HANDLE) + if (chain->backbuffer.image != VK_NULL_HANDLE && vk->context->has_acquired_swapchain) { rp_info.renderPass = vk->render_pass; rp_info.framebuffer = chain->backbuffer.framebuffer; @@ -1874,6 +1874,7 @@ static bool vulkan_frame(void *data, const void *frame, vulkan_filter_chain_end_frame((vulkan_filter_chain_t*)vk->filter_chain, vk->cmd); if (chain->backbuffer.image != VK_NULL_HANDLE && + vk->context->has_acquired_swapchain && (vk->readback.pending || vk->readback.streamed)) { /* We cannot safely read back from an image which