diff --git a/gfx/common/vulkan_common.c b/gfx/common/vulkan_common.c index 970c228380..d7f9d2e239 100644 --- a/gfx/common/vulkan_common.c +++ b/gfx/common/vulkan_common.c @@ -28,6 +28,7 @@ #endif #include "vulkan_common.h" +#include "../../libretro-common/include/retro_timers.h" #include "../../configuration.h" static dylib_t vulkan_library; @@ -2162,6 +2163,7 @@ bool vulkan_surface_create(gfx_ctx_vulkan_data_t *vk, vk, width, height, swap_interval)) return false; + vulkan_acquire_next_image(vk); return true; } @@ -2171,6 +2173,13 @@ void vulkan_present(gfx_ctx_vulkan_data_t *vk, unsigned index) VkResult result = VK_SUCCESS; VkResult err = VK_SUCCESS; + /* We're still waiting for a proper swapchain, so just fake it. */ + if (vk->swapchain == VK_NULL_HANDLE) + { + retro_sleep(10); + return; + } + present.swapchainCount = 1; present.pSwapchains = &vk->swapchain; present.pImageIndices = &index; @@ -2253,18 +2262,71 @@ void vulkan_context_destroy(gfx_ctx_vulkan_data_t *vk, } } +static void vulkan_acquire_clear_fences(gfx_ctx_vulkan_data_t *vk) +{ + unsigned i; + for (i = 0; i < vk->context.num_swapchain_images; i++) + { + if (vk->context.swapchain_fences[i]) + { + vkDestroyFence(vk->context.device, + vk->context.swapchain_fences[i], NULL); + vk->context.swapchain_fences[i] = VK_NULL_HANDLE; + vk->context.swapchain_fences_signalled[i] = false; + } + } +} + +static void vulkan_acquire_wait_fences(gfx_ctx_vulkan_data_t *vk) +{ + VkFenceCreateInfo fence_info = + { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; + + unsigned index = vk->context.current_swapchain_index; + VkFence *next_fence = &vk->context.swapchain_fences[index]; + + if (*next_fence != VK_NULL_HANDLE) + { + if (vk->context.swapchain_fences_signalled[index]) + vkWaitForFences(vk->context.device, 1, next_fence, true, UINT64_MAX); + vkResetFences(vk->context.device, 1, next_fence); + vk->context.swapchain_fences_signalled[index] = false; + } + else + vkCreateFence(vk->context.device, &fence_info, NULL, next_fence); +} + void vulkan_acquire_next_image(gfx_ctx_vulkan_data_t *vk) { unsigned index; VkResult err; VkFence fence; - VkSemaphoreCreateInfo sem_info = - { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; VkFenceCreateInfo fence_info = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; - VkFence *next_fence = NULL; + VkSemaphoreCreateInfo sem_info = + { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; bool is_retrying = false; + if (vk->swapchain == VK_NULL_HANDLE) + { + /* We don't have a swapchain, try to create one now. */ + 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"); + return; + } + + if (vk->swapchain == VK_NULL_HANDLE) + { + /* We still don't have a swapchain, so just fake it ... */ + vk->context.current_swapchain_index = 0; + vulkan_acquire_clear_fences(vk); + vulkan_acquire_wait_fences(vk); + return; + } + } + retry: vkCreateFence(vk->context.device, &fence_info, NULL, &fence); @@ -2281,17 +2343,7 @@ retry: vkWaitForFences(vk->context.device, 1, &fence, true, UINT64_MAX); vkDestroyFence(vk->context.device, fence, NULL); - next_fence = &vk->context.swapchain_fences[index]; - - if (*next_fence != VK_NULL_HANDLE) - { - if (vk->context.swapchain_fences_signalled[index]) - vkWaitForFences(vk->context.device, 1, next_fence, true, UINT64_MAX); - vkResetFences(vk->context.device, 1, next_fence); - vk->context.swapchain_fences_signalled[index] = false; - } - else - vkCreateFence(vk->context.device, &fence_info, NULL, next_fence); + vulkan_acquire_wait_fences(vk); if (err != VK_SUCCESS) { @@ -2430,6 +2482,43 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk, else swapchain_size = surface_properties.currentExtent; +#if 0 + /* Tests for deferred creation. */ + static unsigned retry_count = 0; + if (++retry_count < 50) + { + surface_properties.maxImageExtent.width = 0; + surface_properties.maxImageExtent.height = 0; + surface_properties.minImageExtent.width = 0; + surface_properties.minImageExtent.height = 0; + } +#endif + + /* Clamp swapchain size to boundaries. */ + if (swapchain_size.width > surface_properties.maxImageExtent.width) + swapchain_size.width = surface_properties.maxImageExtent.width; + if (swapchain_size.width < surface_properties.minImageExtent.width) + swapchain_size.width = surface_properties.minImageExtent.width; + if (swapchain_size.height > surface_properties.maxImageExtent.height) + swapchain_size.height = surface_properties.maxImageExtent.height; + if (swapchain_size.height < surface_properties.minImageExtent.height) + swapchain_size.height = surface_properties.minImageExtent.height; + + if (swapchain_size.width == 0 && swapchain_size.height == 0) + { + /* Cannot create swapchain yet, try again later. */ + if (vk->swapchain != VK_NULL_HANDLE) + vkDestroySwapchainKHR(vk->context.device, vk->swapchain, NULL); + vk->swapchain = VK_NULL_HANDLE; + vk->context.swapchain_width = width; + vk->context.swapchain_height = height; + vk->context.num_swapchain_images = 1; + + memset(vk->context.swapchain_images, 0, sizeof(vk->context.swapchain_images)); + RARCH_LOG("[Vulkan]: Cannot create a swapchain yet. Will try again later ...\n"); + return true; + } + RARCH_LOG("[Vulkan]: Using swapchain size %u x %u.\n", swapchain_size.width, swapchain_size.height); @@ -2531,18 +2620,8 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk, RARCH_LOG("[Vulkan]: Got %u swapchain images.\n", vk->context.num_swapchain_images); - for (i = 0; i < vk->context.num_swapchain_images; i++) - { - if (vk->context.swapchain_fences[i]) - { - vkDestroyFence(vk->context.device, - vk->context.swapchain_fences[i], NULL); - vk->context.swapchain_fences[i] = VK_NULL_HANDLE; - vk->context.swapchain_fences_signalled[i] = false; - } - } - - vulkan_acquire_next_image(vk); + vulkan_acquire_clear_fences(vk); + vk->context.invalid_swapchain = true; return true; } diff --git a/gfx/drivers/vulkan.c b/gfx/drivers/vulkan.c index 1e14aeb11b..dafcb40fb7 100644 --- a/gfx/drivers/vulkan.c +++ b/gfx/drivers/vulkan.c @@ -128,7 +128,14 @@ static void vulkan_init_framebuffers( VkFramebufferCreateInfo info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; - vk->swapchain[i].backbuffer.image = vk->context->swapchain_images[i]; + vk->swapchain[i].backbuffer.image = vk->context->swapchain_images[i]; + + if (vk->context->swapchain_images[i] == VK_NULL_HANDLE) + { + vk->swapchain[i].backbuffer.view = VK_NULL_HANDLE; + vk->swapchain[i].backbuffer.framebuffer = VK_NULL_HANDLE; + continue; + } /* Create an image view which we can render into. */ view.viewType = VK_IMAGE_VIEW_TYPE_2D; @@ -716,10 +723,17 @@ static void vulkan_deinit_framebuffers(vk_t *vk) unsigned i; for (i = 0; i < vk->num_swapchain_images; i++) { - vkDestroyFramebuffer(vk->context->device, - vk->swapchain[i].backbuffer.framebuffer, NULL); - vkDestroyImageView(vk->context->device, - vk->swapchain[i].backbuffer.view, NULL); + if (vk->swapchain[i].backbuffer.framebuffer) + { + vkDestroyFramebuffer(vk->context->device, + vk->swapchain[i].backbuffer.framebuffer, NULL); + } + + if (vk->swapchain[i].backbuffer.view) + { + vkDestroyImageView(vk->context->device, + vk->swapchain[i].backbuffer.view, NULL); + } } vkDestroyRenderPass(vk->context->device, vk->render_pass, NULL); @@ -1748,88 +1762,92 @@ static bool vulkan_frame(void *data, const void *frame, (vulkan_filter_chain_t*)vk->filter_chain, vk->cmd, &vk->vk_vp); /* Render to backbuffer. */ - rp_info.renderPass = vk->render_pass; - rp_info.framebuffer = chain->backbuffer.framebuffer; - rp_info.renderArea.extent.width = vk->context->swapchain_width; - rp_info.renderArea.extent.height = vk->context->swapchain_height; - rp_info.clearValueCount = 1; - rp_info.pClearValues = &clear_color; + if (chain->backbuffer.image != VK_NULL_HANDLE) + { + rp_info.renderPass = vk->render_pass; + rp_info.framebuffer = chain->backbuffer.framebuffer; + rp_info.renderArea.extent.width = vk->context->swapchain_width; + rp_info.renderArea.extent.height = vk->context->swapchain_height; + rp_info.clearValueCount = 1; + rp_info.pClearValues = &clear_color; - clear_color.color.float32[0] = 0.0f; - clear_color.color.float32[1] = 0.0f; - clear_color.color.float32[2] = 0.0f; - clear_color.color.float32[3] = 0.0f; + clear_color.color.float32[0] = 0.0f; + clear_color.color.float32[1] = 0.0f; + clear_color.color.float32[2] = 0.0f; + clear_color.color.float32[3] = 0.0f; - /* Prepare backbuffer for rendering. We don't use WSI semaphores here. */ - vulkan_image_layout_transition(vk, vk->cmd, chain->backbuffer.image, - VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); + /* Prepare backbuffer for rendering. We don't use WSI semaphores here. */ + vulkan_image_layout_transition(vk, vk->cmd, chain->backbuffer.image, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); - /* Begin render pass and set up viewport */ - vkCmdBeginRenderPass(vk->cmd, &rp_info, VK_SUBPASS_CONTENTS_INLINE); + /* Begin render pass and set up viewport */ + vkCmdBeginRenderPass(vk->cmd, &rp_info, VK_SUBPASS_CONTENTS_INLINE); - vulkan_filter_chain_build_viewport_pass( - (vulkan_filter_chain_t*)vk->filter_chain, vk->cmd, - &vk->vk_vp, vk->mvp.data); + vulkan_filter_chain_build_viewport_pass( + (vulkan_filter_chain_t*)vk->filter_chain, vk->cmd, + &vk->vk_vp, vk->mvp.data); #if defined(HAVE_MENU) - if (vk->menu.enable) - { - menu_driver_frame(video_info); - - if (vk->menu.textures[vk->menu.last_index].image != VK_NULL_HANDLE) + if (vk->menu.enable) { - struct vk_draw_quad quad; - struct vk_texture *optimal = &vk->menu.textures_optimal[vk->menu.last_index]; - vulkan_set_viewport(vk, width, height, vk->menu.full_screen, false); + menu_driver_frame(video_info); - quad.pipeline = vk->pipelines.alpha_blend; - quad.texture = &vk->menu.textures[vk->menu.last_index]; - - if (optimal->memory != VK_NULL_HANDLE) + if (vk->menu.textures[vk->menu.last_index].image != VK_NULL_HANDLE) { - if (vk->menu.dirty[vk->menu.last_index]) + struct vk_draw_quad quad; + struct vk_texture *optimal = &vk->menu.textures_optimal[vk->menu.last_index]; + vulkan_set_viewport(vk, width, height, vk->menu.full_screen, false); + + quad.pipeline = vk->pipelines.alpha_blend; + quad.texture = &vk->menu.textures[vk->menu.last_index]; + + if (optimal->memory != VK_NULL_HANDLE) { - vulkan_copy_staging_to_dynamic(vk, vk->cmd, - optimal, - quad.texture); - vk->menu.dirty[vk->menu.last_index] = false; + if (vk->menu.dirty[vk->menu.last_index]) + { + vulkan_copy_staging_to_dynamic(vk, vk->cmd, + optimal, + quad.texture); + vk->menu.dirty[vk->menu.last_index] = false; + } + quad.texture = optimal; } - quad.texture = optimal; + + quad.sampler = optimal->mipmap ? + vk->samplers.mipmap_linear : vk->samplers.linear; + + quad.mvp = &vk->mvp_no_rot; + quad.color.r = 1.0f; + quad.color.g = 1.0f; + quad.color.b = 1.0f; + quad.color.a = vk->menu.alpha; + vulkan_draw_quad(vk, &quad); } - - quad.sampler = optimal->mipmap ? - vk->samplers.mipmap_linear : vk->samplers.linear; - - quad.mvp = &vk->mvp_no_rot; - quad.color.r = 1.0f; - quad.color.g = 1.0f; - quad.color.b = 1.0f; - quad.color.a = vk->menu.alpha; - vulkan_draw_quad(vk, &quad); } - } #endif - if (msg) - font_driver_render_msg(video_info, NULL, msg, NULL); + if (msg) + font_driver_render_msg(video_info, NULL, msg, NULL); #ifdef HAVE_OVERLAY - if (vk->overlay.enable) - vulkan_render_overlay(vk, video_info); + if (vk->overlay.enable) + vulkan_render_overlay(vk, video_info); #endif - /* End the render pass. We're done rendering to backbuffer now. */ - vkCmdEndRenderPass(vk->cmd); + /* End the render pass. We're done rendering to backbuffer now. */ + vkCmdEndRenderPass(vk->cmd); + } /* End the filter chain frame. * This must happen outside a render pass. */ vulkan_filter_chain_end_frame((vulkan_filter_chain_t*)vk->filter_chain, vk->cmd); - if (vk->readback.pending || vk->readback.streamed) + if (chain->backbuffer.image != VK_NULL_HANDLE && + (vk->readback.pending || vk->readback.streamed)) { /* We cannot safely read back from an image which * has already been presented as we need to @@ -1860,7 +1878,7 @@ static bool vulkan_frame(void *data, const void *frame, vk->readback.pending = false; } - else + else if (chain->backbuffer.image != VK_NULL_HANDLE) { /* Prepare backbuffer for presentation. */ vulkan_image_layout_transition(vk, vk->cmd, @@ -1930,7 +1948,6 @@ static bool vulkan_frame(void *data, const void *frame, } submit_info.pSignalSemaphores = submit_info.signalSemaphoreCount ? signal_semaphores : NULL; - #ifdef HAVE_THREADS slock_lock(vk->context->queue_lock); #endif diff --git a/gfx/drivers_context/android_ctx.c b/gfx/drivers_context/android_ctx.c index d2212f94c9..341aa465c6 100644 --- a/gfx/drivers_context/android_ctx.c +++ b/gfx/drivers_context/android_ctx.c @@ -281,6 +281,7 @@ static bool android_gfx_ctx_set_resize(void *data, return false; } + vulkan_acquire_next_image(&and->vk); and->vk.context.invalid_swapchain = true; and->vk.need_new_swapchain = false; #endif diff --git a/gfx/drivers_context/khr_display_ctx.c b/gfx/drivers_context/khr_display_ctx.c index 745d770055..53e3bfb528 100644 --- a/gfx/drivers_context/khr_display_ctx.c +++ b/gfx/drivers_context/khr_display_ctx.c @@ -103,6 +103,8 @@ static bool gfx_ctx_khr_display_set_resize(void *data, return false; } + vulkan_acquire_next_image(&khr->vk); + khr->vk.context.invalid_swapchain = true; khr->vk.need_new_swapchain = false; return false; diff --git a/gfx/drivers_context/wayland_ctx.c b/gfx/drivers_context/wayland_ctx.c index 6c7b8be4be..cde0887fb0 100644 --- a/gfx/drivers_context/wayland_ctx.c +++ b/gfx/drivers_context/wayland_ctx.c @@ -684,7 +684,10 @@ static bool gfx_ctx_wl_set_resize(void *data, unsigned width, unsigned height) wl->height = height; if (vulkan_create_swapchain(&wl->vk, width, height, wl->swap_interval)) + { wl->vk.context.invalid_swapchain = true; + vulkan_acquire_next_image(&wl->vk); + } else { RARCH_ERR("[Wayland/Vulkan]: Failed to update swapchain.\n"); diff --git a/gfx/drivers_context/wgl_ctx.c b/gfx/drivers_context/wgl_ctx.c index 39dbb26cdb..9b5e441503 100644 --- a/gfx/drivers_context/wgl_ctx.c +++ b/gfx/drivers_context/wgl_ctx.c @@ -395,6 +395,7 @@ static bool gfx_ctx_wgl_set_resize(void *data, return false; } + vulkan_acquire_next_image(&win32_vk); win32_vk.context.invalid_swapchain = true; win32_vk.need_new_swapchain = false; #endif diff --git a/gfx/drivers_context/x_ctx.c b/gfx/drivers_context/x_ctx.c index 08e6b7bcef..1faa28b95f 100644 --- a/gfx/drivers_context/x_ctx.c +++ b/gfx/drivers_context/x_ctx.c @@ -448,6 +448,7 @@ static bool gfx_ctx_x_set_resize(void *data, return false; } + vulkan_acquire_next_image(&x->vk); x->vk.context.invalid_swapchain = true; x->vk.need_new_swapchain = false; }