Support pending swapchain creation.

This commit is contained in:
Themaister 2017-12-09 12:58:11 +01:00
parent ac42b87400
commit 75b84b4f94
7 changed files with 194 additions and 90 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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");

View File

@ -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

View File

@ -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;
}