Merge pull request #10821 from Themaister/wsi-rework-semaphores

Vulkan: Refactor WSI handling
This commit is contained in:
Autechre 2020-06-09 05:56:34 +02:00 committed by GitHub
commit 60bdd1d323
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 231 additions and 78 deletions

View File

@ -42,7 +42,7 @@
#define VENDOR_ID_NV 0x10DE #define VENDOR_ID_NV 0x10DE
#define VENDOR_ID_INTEL 0x8086 #define VENDOR_ID_INTEL 0x8086
#if defined(_WIN32) || defined(ANDROID) #if defined(_WIN32)
#define VULKAN_EMULATE_MAILBOX #define VULKAN_EMULATE_MAILBOX
#endif #endif
@ -1675,10 +1675,6 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk)
"VK_KHR_sampler_mirror_clamp_to_edge", "VK_KHR_sampler_mirror_clamp_to_edge",
}; };
#ifdef VULKAN_DEBUG
static const char *device_layers[] = { "VK_LAYER_LUNARG_standard_validation" };
#endif
struct retro_hw_render_context_negotiation_interface_vulkan *iface = struct retro_hw_render_context_negotiation_interface_vulkan *iface =
(struct retro_hw_render_context_negotiation_interface_vulkan*)video_driver_get_context_negotiation_interface(); (struct retro_hw_render_context_negotiation_interface_vulkan*)video_driver_get_context_negotiation_interface();
@ -1705,13 +1701,8 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk)
vulkan_symbol_wrapper_instance_proc_addr(), vulkan_symbol_wrapper_instance_proc_addr(),
device_extensions, device_extensions,
ARRAY_SIZE(device_extensions), ARRAY_SIZE(device_extensions),
#ifdef VULKAN_DEBUG
device_layers,
ARRAY_SIZE(device_layers),
#else
NULL, NULL,
0, 0,
#endif
&features); &features);
if (!ret) if (!ret)
@ -1755,6 +1746,22 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk)
vk->emulate_mailbox = vk->fullscreen; vk->emulate_mailbox = vk->fullscreen;
#endif #endif
/* If we're emulating mailbox, stick to using fences rather than semaphores.
* Avoids some really weird driver bugs. */
if (!vk->emulate_mailbox)
{
if (vk->context.gpu_properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU)
{
vk->use_wsi_semaphore = true;
RARCH_LOG("[Vulkan]: Using semaphores for WSI acquire.\n");
}
else
{
vk->use_wsi_semaphore = false;
RARCH_LOG("[Vulkan]: Using fences for WSI acquire.\n");
}
}
RARCH_LOG("[Vulkan]: Using GPU: %s\n", vk->context.gpu_properties.deviceName); RARCH_LOG("[Vulkan]: Using GPU: %s\n", vk->context.gpu_properties.deviceName);
{ {
@ -1861,10 +1868,6 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk)
device_info.enabledExtensionCount = enabled_device_extension_count; device_info.enabledExtensionCount = enabled_device_extension_count;
device_info.ppEnabledExtensionNames = enabled_device_extension_count ? enabled_device_extensions : NULL; device_info.ppEnabledExtensionNames = enabled_device_extension_count ? enabled_device_extensions : NULL;
device_info.pEnabledFeatures = &features; device_info.pEnabledFeatures = &features;
#ifdef VULKAN_DEBUG
device_info.enabledLayerCount = ARRAY_SIZE(device_layers);
device_info.ppEnabledLayerNames = device_layers;
#endif
if (cached_device_vk) if (cached_device_vk)
{ {
@ -1917,7 +1920,7 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk,
#ifdef VULKAN_DEBUG #ifdef VULKAN_DEBUG
instance_extensions[ext_count++] = "VK_EXT_debug_report"; instance_extensions[ext_count++] = "VK_EXT_debug_report";
static const char *instance_layers[] = { "VK_LAYER_LUNARG_standard_validation" }; static const char *instance_layers[] = { "VK_LAYER_KHRONOS_validation" };
#endif #endif
bool use_instance_ext; bool use_instance_ext;
@ -2568,10 +2571,28 @@ static void vulkan_destroy_swapchain(gfx_ctx_vulkan_data_t *vk)
if (vk->context.swapchain_fences[i] != VK_NULL_HANDLE) if (vk->context.swapchain_fences[i] != VK_NULL_HANDLE)
vkDestroyFence(vk->context.device, vkDestroyFence(vk->context.device,
vk->context.swapchain_fences[i], NULL); vk->context.swapchain_fences[i], NULL);
if (vk->context.swapchain_recycled_semaphores[i] != VK_NULL_HANDLE)
vkDestroySemaphore(vk->context.device,
vk->context.swapchain_recycled_semaphores[i], NULL);
if (vk->context.swapchain_wait_semaphores[i] != VK_NULL_HANDLE)
vkDestroySemaphore(vk->context.device,
vk->context.swapchain_wait_semaphores[i], NULL);
} }
memset(vk->context.swapchain_semaphores, 0, sizeof(vk->context.swapchain_semaphores)); if (vk->context.swapchain_acquire_semaphore != VK_NULL_HANDLE)
memset(vk->context.swapchain_fences, 0, sizeof(vk->context.swapchain_fences)); vkDestroySemaphore(vk->context.device,
vk->context.swapchain_acquire_semaphore, NULL);
vk->context.swapchain_acquire_semaphore = VK_NULL_HANDLE;
memset(vk->context.swapchain_semaphores, 0,
sizeof(vk->context.swapchain_semaphores));
memset(vk->context.swapchain_recycled_semaphores, 0,
sizeof(vk->context.swapchain_recycled_semaphores));
memset(vk->context.swapchain_wait_semaphores, 0,
sizeof(vk->context.swapchain_wait_semaphores));
memset(vk->context.swapchain_fences, 0,
sizeof(vk->context.swapchain_fences));
vk->context.num_recycled_acquire_semaphores = 0;
} }
void vulkan_present(gfx_ctx_vulkan_data_t *vk, unsigned index) void vulkan_present(gfx_ctx_vulkan_data_t *vk, unsigned index)
@ -2686,6 +2707,12 @@ void vulkan_context_destroy(gfx_ctx_vulkan_data_t *vk,
} }
} }
static void vulkan_recycle_acquire_semaphore(struct vulkan_context *ctx, VkSemaphore sem)
{
assert(ctx->num_recycled_acquire_semaphores < VULKAN_MAX_SWAPCHAIN_IMAGES);
ctx->swapchain_recycled_semaphores[ctx->num_recycled_acquire_semaphores++] = sem;
}
static void vulkan_acquire_clear_fences(gfx_ctx_vulkan_data_t *vk) static void vulkan_acquire_clear_fences(gfx_ctx_vulkan_data_t *vk)
{ {
unsigned i; unsigned i;
@ -2698,7 +2725,29 @@ static void vulkan_acquire_clear_fences(gfx_ctx_vulkan_data_t *vk)
vk->context.swapchain_fences[i] = VK_NULL_HANDLE; vk->context.swapchain_fences[i] = VK_NULL_HANDLE;
} }
vk->context.swapchain_fences_signalled[i] = false; vk->context.swapchain_fences_signalled[i] = false;
if (vk->context.swapchain_wait_semaphores[i])
vulkan_recycle_acquire_semaphore(&vk->context, vk->context.swapchain_wait_semaphores[i]);
vk->context.swapchain_wait_semaphores[i] = VK_NULL_HANDLE;
} }
vk->context.current_frame_index = 0;
}
static VkSemaphore vulkan_get_wsi_acquire_semaphore(struct vulkan_context *ctx)
{
if (ctx->num_recycled_acquire_semaphores == 0)
{
VkSemaphoreCreateInfo sem_info = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
vkCreateSemaphore(ctx->device, &sem_info, NULL,
&ctx->swapchain_recycled_semaphores[ctx->num_recycled_acquire_semaphores++]);
}
VkSemaphore sem =
ctx->swapchain_recycled_semaphores[--ctx->num_recycled_acquire_semaphores];
ctx->swapchain_recycled_semaphores[ctx->num_recycled_acquire_semaphores] =
VK_NULL_HANDLE;
return sem;
} }
static void vulkan_acquire_wait_fences(gfx_ctx_vulkan_data_t *vk) static void vulkan_acquire_wait_fences(gfx_ctx_vulkan_data_t *vk)
@ -2706,7 +2755,12 @@ static void vulkan_acquire_wait_fences(gfx_ctx_vulkan_data_t *vk)
VkFenceCreateInfo fence_info = VkFenceCreateInfo fence_info =
{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
unsigned index = vk->context.current_swapchain_index; /* Decouples the frame fence index from swapchain index. */
vk->context.current_frame_index =
(vk->context.current_frame_index + 1) %
vk->context.num_swapchain_images;
unsigned index = vk->context.current_frame_index;
VkFence *next_fence = &vk->context.swapchain_fences[index]; VkFence *next_fence = &vk->context.swapchain_fences[index];
if (*next_fence != VK_NULL_HANDLE) if (*next_fence != VK_NULL_HANDLE)
@ -2718,6 +2772,10 @@ static void vulkan_acquire_wait_fences(gfx_ctx_vulkan_data_t *vk)
else else
vkCreateFence(vk->context.device, &fence_info, NULL, next_fence); vkCreateFence(vk->context.device, &fence_info, NULL, next_fence);
vk->context.swapchain_fences_signalled[index] = false; vk->context.swapchain_fences_signalled[index] = false;
if (vk->context.swapchain_wait_semaphores[index] != VK_NULL_HANDLE)
vulkan_recycle_acquire_semaphore(&vk->context, vk->context.swapchain_wait_semaphores[index]);
vk->context.swapchain_wait_semaphores[index] = VK_NULL_HANDLE;
} }
static void vulkan_create_wait_fences(gfx_ctx_vulkan_data_t *vk) static void vulkan_create_wait_fences(gfx_ctx_vulkan_data_t *vk)
@ -2732,13 +2790,16 @@ static void vulkan_create_wait_fences(gfx_ctx_vulkan_data_t *vk)
vkCreateFence(vk->context.device, &fence_info, NULL, vkCreateFence(vk->context.device, &fence_info, NULL,
&vk->context.swapchain_fences[i]); &vk->context.swapchain_fences[i]);
} }
vk->context.current_frame_index = 0;
} }
void vulkan_acquire_next_image(gfx_ctx_vulkan_data_t *vk) void vulkan_acquire_next_image(gfx_ctx_vulkan_data_t *vk)
{ {
unsigned index; unsigned index;
VkResult err; VkResult err;
VkFence fence; VkFence fence = VK_NULL_HANDLE;
VkSemaphore semaphore = VK_NULL_HANDLE;
VkFenceCreateInfo fence_info = VkFenceCreateInfo fence_info =
{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
VkSemaphoreCreateInfo sem_info = VkSemaphoreCreateInfo sem_info =
@ -2760,6 +2821,7 @@ retry:
{ {
/* We still don't have a swapchain, so just fake it ... */ /* We still don't have a swapchain, so just fake it ... */
vk->context.current_swapchain_index = 0; vk->context.current_swapchain_index = 0;
vk->context.current_frame_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; vk->context.invalid_swapchain = true;
@ -2776,31 +2838,54 @@ retry:
* MAILBOX would do. */ * MAILBOX would do. */
err = vulkan_emulated_mailbox_acquire_next_image( err = vulkan_emulated_mailbox_acquire_next_image(
&vk->mailbox, &vk->context.current_swapchain_index); &vk->mailbox, &vk->context.current_swapchain_index);
fence = VK_NULL_HANDLE;
} }
else else
{ {
if (vk->use_wsi_semaphore)
semaphore = vulkan_get_wsi_acquire_semaphore(&vk->context);
else
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); semaphore, fence, &vk->context.current_swapchain_index);
#ifdef ANDROID
/* VK_SUBOPTIMAL_KHR can be returned on Android 10 /* VK_SUBOPTIMAL_KHR can be returned on Android 10
* when prerotate is not dealt with. * when prerotate is not dealt with.
* This is not an error we need to care about, and * This is not an error we need to care about, and
* we'll treat it as SUCCESS. */ * we'll treat it as SUCCESS. */
if (err == VK_SUBOPTIMAL_KHR) if (err == VK_SUBOPTIMAL_KHR)
err = VK_SUCCESS; err = VK_SUCCESS;
#endif
} }
if (err == VK_SUCCESS) if (err == VK_SUCCESS || err == VK_SUBOPTIMAL_KHR)
{ {
if (fence != VK_NULL_HANDLE) if (fence != VK_NULL_HANDLE)
vkWaitForFences(vk->context.device, 1, &fence, true, UINT64_MAX); vkWaitForFences(vk->context.device, 1, &fence, true, UINT64_MAX);
vk->context.has_acquired_swapchain = true; vk->context.has_acquired_swapchain = true;
if (vk->context.swapchain_acquire_semaphore)
{
#ifdef HAVE_THREADS
slock_lock(vk->context.queue_lock);
#endif
RARCH_LOG("[Vulkan]: Destroying stale acquire semaphore.\n");
vkDeviceWaitIdle(vk->context.device);
vkDestroySemaphore(vk->context.device, vk->context.swapchain_acquire_semaphore, NULL);
#ifdef HAVE_THREADS
slock_unlock(vk->context.queue_lock);
#endif
}
vk->context.swapchain_acquire_semaphore = semaphore;
} }
else else
{
vk->context.has_acquired_swapchain = false; vk->context.has_acquired_swapchain = false;
if (semaphore)
vulkan_recycle_acquire_semaphore(&vk->context, semaphore);
}
#ifdef WSI_HARDENING_TEST #ifdef WSI_HARDENING_TEST
trigger_spurious_error_vkresult(&err); trigger_spurious_error_vkresult(&err);
@ -2811,11 +2896,9 @@ retry:
if (err == VK_NOT_READY || err == VK_TIMEOUT) if (err == VK_NOT_READY || err == VK_TIMEOUT)
{ {
/* Just pretend we have a swapchain index, round-robin style. */ /* Do nothing. */
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) else if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR)
{ {
/* Throw away the old swapchain and try again. */ /* Throw away the old swapchain and try again. */
vulkan_destroy_swapchain(vk); vulkan_destroy_swapchain(vk);

View File

@ -102,6 +102,7 @@ typedef struct vulkan_context
uint32_t graphics_queue_index; uint32_t graphics_queue_index;
uint32_t num_swapchain_images; uint32_t num_swapchain_images;
uint32_t current_swapchain_index; uint32_t current_swapchain_index;
uint32_t current_frame_index;
VkInstance instance; VkInstance instance;
VkPhysicalDevice gpu; VkPhysicalDevice gpu;
@ -117,6 +118,11 @@ typedef struct vulkan_context
VkSemaphore swapchain_semaphores[VULKAN_MAX_SWAPCHAIN_IMAGES]; VkSemaphore swapchain_semaphores[VULKAN_MAX_SWAPCHAIN_IMAGES];
VkFormat swapchain_format; VkFormat swapchain_format;
VkSemaphore swapchain_acquire_semaphore;
unsigned num_recycled_acquire_semaphores;
VkSemaphore swapchain_recycled_semaphores[VULKAN_MAX_SWAPCHAIN_IMAGES];
VkSemaphore swapchain_wait_semaphores[VULKAN_MAX_SWAPCHAIN_IMAGES];
slock_t *queue_lock; slock_t *queue_lock;
retro_vulkan_destroy_device_t destroy_device; retro_vulkan_destroy_device_t destroy_device;
@ -153,6 +159,10 @@ typedef struct gfx_ctx_vulkan_data
bool created_new_swapchain; bool created_new_swapchain;
bool emulate_mailbox; bool emulate_mailbox;
bool emulating_mailbox; bool emulating_mailbox;
/* If set, prefer a path where we use
* semaphores instead of fences for vkAcquireNextImageKHR.
* Helps workaround certain performance issues on some drivers. */
bool use_wsi_semaphore;
vulkan_context_t context; vulkan_context_t context;
VkSurfaceKHR vk_surface; VkSurfaceKHR vk_surface;
VkSwapchainKHR swapchain; VkSwapchainKHR swapchain;
@ -285,7 +295,6 @@ struct vk_descriptor_manager
struct vk_per_frame struct vk_per_frame
{ {
struct vk_image backbuffer;
struct vk_texture texture; struct vk_texture texture;
struct vk_texture texture_optimal; struct vk_texture texture_optimal;
struct vk_buffer_chain vbo; struct vk_buffer_chain vbo;
@ -344,7 +353,9 @@ typedef struct vk
VkRenderPass render_pass; VkRenderPass render_pass;
struct video_viewport vp; struct video_viewport vp;
struct vk_per_frame *chain; struct vk_per_frame *chain;
struct vk_image *backbuffer;
struct vk_per_frame swapchain[VULKAN_MAX_SWAPCHAIN_IMAGES]; struct vk_per_frame swapchain[VULKAN_MAX_SWAPCHAIN_IMAGES];
struct vk_image backbuffers[VULKAN_MAX_SWAPCHAIN_IMAGES];
struct vk_texture default_texture; struct vk_texture default_texture;
/* Currently active command buffer. */ /* Currently active command buffer. */
@ -419,7 +430,7 @@ typedef struct vk
struct retro_hw_render_interface_vulkan iface; struct retro_hw_render_interface_vulkan iface;
const struct retro_vulkan_image *image; const struct retro_vulkan_image *image;
const VkSemaphore *semaphores; VkSemaphore *semaphores;
VkSemaphore signal_semaphore; VkSemaphore signal_semaphore;
VkPipelineStageFlags *wait_dst_stages; VkPipelineStageFlags *wait_dst_stages;
VkCommandBuffer *cmd; VkCommandBuffer *cmd;

View File

@ -136,19 +136,19 @@ static void vulkan_init_framebuffers(
VkFramebufferCreateInfo info = VkFramebufferCreateInfo info =
{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
vk->swapchain[i].backbuffer.image = vk->context->swapchain_images[i]; vk->backbuffers[i].image = vk->context->swapchain_images[i];
if (vk->context->swapchain_images[i] == VK_NULL_HANDLE) if (vk->context->swapchain_images[i] == VK_NULL_HANDLE)
{ {
vk->swapchain[i].backbuffer.view = VK_NULL_HANDLE; vk->backbuffers[i].view = VK_NULL_HANDLE;
vk->swapchain[i].backbuffer.framebuffer = VK_NULL_HANDLE; vk->backbuffers[i].framebuffer = VK_NULL_HANDLE;
continue; continue;
} }
/* Create an image view which we can render into. */ /* Create an image view which we can render into. */
view.viewType = VK_IMAGE_VIEW_TYPE_2D; view.viewType = VK_IMAGE_VIEW_TYPE_2D;
view.format = vk->context->swapchain_format; view.format = vk->context->swapchain_format;
view.image = vk->swapchain[i].backbuffer.image; view.image = vk->backbuffers[i].image;
view.subresourceRange.baseMipLevel = 0; view.subresourceRange.baseMipLevel = 0;
view.subresourceRange.baseArrayLayer = 0; view.subresourceRange.baseArrayLayer = 0;
view.subresourceRange.levelCount = 1; view.subresourceRange.levelCount = 1;
@ -160,18 +160,18 @@ static void vulkan_init_framebuffers(
view.components.a = VK_COMPONENT_SWIZZLE_A; view.components.a = VK_COMPONENT_SWIZZLE_A;
vkCreateImageView(vk->context->device, vkCreateImageView(vk->context->device,
&view, NULL, &vk->swapchain[i].backbuffer.view); &view, NULL, &vk->backbuffers[i].view);
/* Create the framebuffer */ /* Create the framebuffer */
info.renderPass = vk->render_pass; info.renderPass = vk->render_pass;
info.attachmentCount = 1; info.attachmentCount = 1;
info.pAttachments = &vk->swapchain[i].backbuffer.view; info.pAttachments = &vk->backbuffers[i].view;
info.width = vk->context->swapchain_width; info.width = vk->context->swapchain_width;
info.height = vk->context->swapchain_height; info.height = vk->context->swapchain_height;
info.layers = 1; info.layers = 1;
vkCreateFramebuffer(vk->context->device, vkCreateFramebuffer(vk->context->device,
&info, NULL, &vk->swapchain[i].backbuffer.framebuffer); &info, NULL, &vk->backbuffers[i].framebuffer);
} }
} }
@ -739,16 +739,16 @@ static void vulkan_deinit_framebuffers(vk_t *vk)
unsigned i; unsigned i;
for (i = 0; i < vk->num_swapchain_images; i++) for (i = 0; i < vk->num_swapchain_images; i++)
{ {
if (vk->swapchain[i].backbuffer.framebuffer) if (vk->backbuffers[i].framebuffer)
{ {
vkDestroyFramebuffer(vk->context->device, vkDestroyFramebuffer(vk->context->device,
vk->swapchain[i].backbuffer.framebuffer, NULL); vk->backbuffers[i].framebuffer, NULL);
} }
if (vk->swapchain[i].backbuffer.view) if (vk->backbuffers[i].view)
{ {
vkDestroyImageView(vk->context->device, vkDestroyImageView(vk->context->device,
vk->swapchain[i].backbuffer.view, NULL); vk->backbuffers[i].view, NULL);
} }
} }
@ -769,7 +769,7 @@ static bool vulkan_init_default_filter_chain(vk_t *vk)
info.memory_properties = &vk->context->memory_properties; info.memory_properties = &vk->context->memory_properties;
info.pipeline_cache = vk->pipelines.cache; info.pipeline_cache = vk->pipelines.cache;
info.queue = vk->context->queue; info.queue = vk->context->queue;
info.command_pool = vk->swapchain[vk->context->current_swapchain_index].cmd_pool; info.command_pool = vk->swapchain[vk->context->current_frame_index].cmd_pool;
info.max_input_size.width = vk->tex_w; info.max_input_size.width = vk->tex_w;
info.max_input_size.height = vk->tex_h; info.max_input_size.height = vk->tex_h;
info.swapchain.viewport = vk->vk_vp; info.swapchain.viewport = vk->vk_vp;
@ -803,7 +803,7 @@ static bool vulkan_init_filter_chain_preset(vk_t *vk, const char *shader_path)
info.memory_properties = &vk->context->memory_properties; info.memory_properties = &vk->context->memory_properties;
info.pipeline_cache = vk->pipelines.cache; info.pipeline_cache = vk->pipelines.cache;
info.queue = vk->context->queue; info.queue = vk->context->queue;
info.command_pool = vk->swapchain[vk->context->current_swapchain_index].cmd_pool; info.command_pool = vk->swapchain[vk->context->current_frame_index].cmd_pool;
info.max_input_size.width = vk->tex_w; info.max_input_size.width = vk->tex_w;
info.max_input_size.height = vk->tex_h; info.max_input_size.height = vk->tex_h;
info.swapchain.viewport = vk->vk_vp; info.swapchain.viewport = vk->vk_vp;
@ -909,6 +909,7 @@ static void vulkan_deinit_static_resources(vk_t *vk)
vk->staging_pool, NULL); vk->staging_pool, NULL);
free(vk->hw.cmd); free(vk->hw.cmd);
free(vk->hw.wait_dst_stages); free(vk->hw.wait_dst_stages);
free(vk->hw.semaphores);
for (i = 0; i < VULKAN_MAX_SWAPCHAIN_IMAGES; i++) for (i = 0; i < VULKAN_MAX_SWAPCHAIN_IMAGES; i++)
if (vk->readback.staging[i].memory != VK_NULL_HANDLE) if (vk->readback.staging[i].memory != VK_NULL_HANDLE)
@ -982,7 +983,7 @@ static void vulkan_free(void *data)
static uint32_t vulkan_get_sync_index(void *handle) static uint32_t vulkan_get_sync_index(void *handle)
{ {
vk_t *vk = (vk_t*)handle; vk_t *vk = (vk_t*)handle;
return vk->context->current_swapchain_index; return vk->context->current_frame_index;
} }
static uint32_t vulkan_get_sync_index_mask(void *handle) static uint32_t vulkan_get_sync_index_mask(void *handle)
@ -1002,21 +1003,27 @@ static void vulkan_set_image(void *handle,
vk->hw.image = image; vk->hw.image = image;
vk->hw.num_semaphores = num_semaphores; vk->hw.num_semaphores = num_semaphores;
vk->hw.semaphores = semaphores;
if (num_semaphores > 0) if (num_semaphores > 0)
{ {
VkPipelineStageFlags *stage_flags = (VkPipelineStageFlags*) /* Allocate one extra in case we need to use WSI acquire semaphores. */
realloc(vk->hw.wait_dst_stages, VkPipelineStageFlags *stage_flags = (VkPipelineStageFlags*)realloc(vk->hw.wait_dst_stages,
sizeof(VkPipelineStageFlags) * vk->hw.num_semaphores); sizeof(VkPipelineStageFlags) * (vk->hw.num_semaphores + 1));
VkSemaphore *new_semaphores = (VkSemaphore*)realloc(vk->hw.semaphores,
sizeof(VkSemaphore) * (vk->hw.num_semaphores + 1));
/* If this fails, we're screwed anyways. */ /* If this fails, we're screwed anyways. */
retro_assert(stage_flags); retro_assert(stage_flags && new_semaphores);
vk->hw.wait_dst_stages = stage_flags; vk->hw.wait_dst_stages = stage_flags;
vk->hw.semaphores = new_semaphores;
for (i = 0; i < vk->hw.num_semaphores; i++) for (i = 0; i < vk->hw.num_semaphores; i++)
{
vk->hw.wait_dst_stages[i] = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; vk->hw.wait_dst_stages[i] = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
vk->hw.semaphores[i] = semaphores[i];
}
vk->hw.valid_semaphore = true; vk->hw.valid_semaphore = true;
vk->hw.src_queue_family = src_queue_family; vk->hw.src_queue_family = src_queue_family;
@ -1585,14 +1592,14 @@ static void vulkan_readback(vk_t *vk)
region.imageExtent.height = vp.height; region.imageExtent.height = vp.height;
region.imageExtent.depth = 1; region.imageExtent.depth = 1;
staging = &vk->readback.staging[vk->context->current_swapchain_index]; staging = &vk->readback.staging[vk->context->current_frame_index];
*staging = vulkan_create_texture(vk, *staging = vulkan_create_texture(vk,
staging->memory != VK_NULL_HANDLE ? staging : NULL, staging->memory != VK_NULL_HANDLE ? staging : NULL,
vk->vp.width, vk->vp.height, vk->vp.width, vk->vp.height,
VK_FORMAT_B8G8R8A8_UNORM, /* Formats don't matter for readback since it's a raw copy. */ VK_FORMAT_B8G8R8A8_UNORM, /* Formats don't matter for readback since it's a raw copy. */
NULL, NULL, VULKAN_TEXTURE_READBACK); NULL, NULL, VULKAN_TEXTURE_READBACK);
vkCmdCopyImageToBuffer(vk->cmd, vk->chain->backbuffer.image, vkCmdCopyImageToBuffer(vk->cmd, vk->backbuffer->image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
staging->buffer, staging->buffer,
1, &region); 1, &region);
@ -1616,24 +1623,26 @@ static void vulkan_inject_black_frame(vk_t *vk, video_frame_info_t *video_info,
const VkClearColorValue clear_color = {{ 0.0f, 0.0f, 0.0f, 1.0f }}; const VkClearColorValue clear_color = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
const VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; const VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
unsigned frame_index = vk->context->current_swapchain_index; unsigned frame_index = vk->context->current_frame_index;
unsigned swapchain_index = vk->context->current_swapchain_index;
struct vk_per_frame *chain = &vk->swapchain[frame_index]; struct vk_per_frame *chain = &vk->swapchain[frame_index];
struct vk_image *backbuffer = &vk->backbuffers[swapchain_index];
vk->chain = chain; vk->chain = chain;
vk->cmd = chain->cmd; vk->cmd = chain->cmd;
begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkResetCommandBuffer(vk->cmd, 0); vkResetCommandBuffer(vk->cmd, 0);
vkBeginCommandBuffer(vk->cmd, &begin_info); vkBeginCommandBuffer(vk->cmd, &begin_info);
vulkan_image_layout_transition(vk, vk->cmd, chain->backbuffer.image, vulkan_image_layout_transition(vk, vk->cmd, backbuffer->image,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
0, VK_ACCESS_TRANSFER_WRITE_BIT, 0, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT); VK_PIPELINE_STAGE_TRANSFER_BIT);
vkCmdClearColorImage(vk->cmd, chain->backbuffer.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, vkCmdClearColorImage(vk->cmd, backbuffer->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
&clear_color, 1, &range); &clear_color, 1, &range);
vulkan_image_layout_transition(vk, vk->cmd, chain->backbuffer.image, vulkan_image_layout_transition(vk, vk->cmd, backbuffer->image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_MEMORY_READ_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
@ -1643,10 +1652,26 @@ static void vulkan_inject_black_frame(vk_t *vk, video_frame_info_t *video_info,
submit_info.commandBufferCount = 1; submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &vk->cmd; submit_info.pCommandBuffers = &vk->cmd;
if (vk->context->swapchain_semaphores[frame_index] != VK_NULL_HANDLE) if (vk->context->has_acquired_swapchain &&
vk->context->swapchain_semaphores[swapchain_index] != VK_NULL_HANDLE)
{ {
submit_info.signalSemaphoreCount = 1; submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = &vk->context->swapchain_semaphores[frame_index]; submit_info.pSignalSemaphores = &vk->context->swapchain_semaphores[swapchain_index];
}
if (vk->context->has_acquired_swapchain &&
vk->context->swapchain_acquire_semaphore != VK_NULL_HANDLE)
{
static const VkPipelineStageFlags wait_stage =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
assert(!vk->context->swapchain_wait_semaphores[frame_index]);
vk->context->swapchain_wait_semaphores[frame_index] =
vk->context->swapchain_acquire_semaphore;
vk->context->swapchain_acquire_semaphore = VK_NULL_HANDLE;
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &vk->context->swapchain_wait_semaphores[frame_index];
submit_info.pWaitDstStageMask = &wait_stage;
} }
#ifdef HAVE_THREADS #ifdef HAVE_THREADS
@ -1671,6 +1696,7 @@ static bool vulkan_frame(void *data, const void *frame,
VkSemaphore signal_semaphores[2]; VkSemaphore signal_semaphores[2];
vk_t *vk = (vk_t*)data; vk_t *vk = (vk_t*)data;
struct vk_per_frame *chain = NULL; struct vk_per_frame *chain = NULL;
struct vk_image *backbuffer = NULL;
bool waits_for_semaphores = false; bool waits_for_semaphores = false;
unsigned width = video_info->width; unsigned width = video_info->width;
unsigned height = video_info->height; unsigned height = video_info->height;
@ -1694,11 +1720,15 @@ static bool vulkan_frame(void *data, const void *frame,
VkSubmitInfo submit_info = { VkSubmitInfo submit_info = {
VK_STRUCTURE_TYPE_SUBMIT_INFO }; VK_STRUCTURE_TYPE_SUBMIT_INFO };
unsigned frame_index = unsigned frame_index =
vk->context->current_frame_index;
unsigned swapchain_index =
vk->context->current_swapchain_index; vk->context->current_swapchain_index;
/* Bookkeeping on start of frame. */ /* Bookkeeping on start of frame. */
chain = &vk->swapchain[frame_index]; chain = &vk->swapchain[frame_index];
backbuffer = &vk->backbuffers[swapchain_index];
vk->chain = chain; vk->chain = chain;
vk->backbuffer = backbuffer;
{ {
struct vk_descriptor_manager *manager = &chain->descriptor_manager; struct vk_descriptor_manager *manager = &chain->descriptor_manager;
@ -1901,10 +1931,10 @@ static bool vulkan_frame(void *data, const void *frame,
#endif #endif
/* Render to backbuffer. */ /* Render to backbuffer. */
if (chain->backbuffer.image != VK_NULL_HANDLE && vk->context->has_acquired_swapchain) if (backbuffer->image != VK_NULL_HANDLE && vk->context->has_acquired_swapchain)
{ {
rp_info.renderPass = vk->render_pass; rp_info.renderPass = vk->render_pass;
rp_info.framebuffer = chain->backbuffer.framebuffer; rp_info.framebuffer = backbuffer->framebuffer;
rp_info.renderArea.extent.width = vk->context->swapchain_width; rp_info.renderArea.extent.width = vk->context->swapchain_width;
rp_info.renderArea.extent.height = vk->context->swapchain_height; rp_info.renderArea.extent.height = vk->context->swapchain_height;
rp_info.clearValueCount = 1; rp_info.clearValueCount = 1;
@ -1915,11 +1945,11 @@ static bool vulkan_frame(void *data, const void *frame,
clear_color.color.float32[2] = 0.0f; clear_color.color.float32[2] = 0.0f;
clear_color.color.float32[3] = 0.0f; clear_color.color.float32[3] = 0.0f;
/* Prepare backbuffer for rendering. We don't use WSI semaphores here. */ /* Prepare backbuffer for rendering. */
vulkan_image_layout_transition(vk, vk->cmd, chain->backbuffer.image, vulkan_image_layout_transition(vk, vk->cmd, backbuffer->image,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, 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,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
/* Begin render pass and set up viewport */ /* Begin render pass and set up viewport */
@ -2000,7 +2030,7 @@ static bool vulkan_frame(void *data, const void *frame,
*/ */
vulkan_filter_chain_end_frame((vulkan_filter_chain_t*)vk->filter_chain, vk->cmd); vulkan_filter_chain_end_frame((vulkan_filter_chain_t*)vk->filter_chain, vk->cmd);
if (chain->backbuffer.image != VK_NULL_HANDLE && if (backbuffer->image != VK_NULL_HANDLE &&
vk->context->has_acquired_swapchain && vk->context->has_acquired_swapchain &&
(vk->readback.pending || vk->readback.streamed)) (vk->readback.pending || vk->readback.streamed))
{ {
@ -2011,19 +2041,19 @@ static bool vulkan_frame(void *data, const void *frame,
* If we're reading back, perform the readback before presenting. * If we're reading back, perform the readback before presenting.
*/ */
vulkan_image_layout_transition(vk, vulkan_image_layout_transition(vk,
vk->cmd, chain->backbuffer.image, vk->cmd, backbuffer->image,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT,
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT); VK_PIPELINE_STAGE_TRANSFER_BIT);
vulkan_readback(vk); vulkan_readback(vk);
/* Prepare for presentation after transfers are complete. */ /* Prepare for presentation after transfers are complete. */
vulkan_image_layout_transition(vk, vk->cmd, vulkan_image_layout_transition(vk, vk->cmd,
chain->backbuffer.image, backbuffer->image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
0, 0,
@ -2033,17 +2063,17 @@ static bool vulkan_frame(void *data, const void *frame,
vk->readback.pending = false; vk->readback.pending = false;
} }
else if (chain->backbuffer.image != VK_NULL_HANDLE && else if (backbuffer->image != VK_NULL_HANDLE &&
vk->context->has_acquired_swapchain) vk->context->has_acquired_swapchain)
{ {
/* Prepare backbuffer for presentation. */ /* Prepare backbuffer for presentation. */
vulkan_image_layout_transition(vk, vk->cmd, vulkan_image_layout_transition(vk, vk->cmd,
chain->backbuffer.image, backbuffer->image,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_MEMORY_READ_BIT, 0,
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
} }
@ -2090,14 +2120,43 @@ static bool vulkan_frame(void *data, const void *frame,
/* Consume the semaphores. */ /* Consume the semaphores. */
vk->hw.valid_semaphore = false; vk->hw.valid_semaphore = false;
/* We allocated space for this. */
if (vk->context->has_acquired_swapchain &&
vk->context->swapchain_acquire_semaphore != VK_NULL_HANDLE)
{
assert(!vk->context->swapchain_wait_semaphores[frame_index]);
vk->context->swapchain_wait_semaphores[frame_index] =
vk->context->swapchain_acquire_semaphore;
vk->context->swapchain_acquire_semaphore = VK_NULL_HANDLE;
vk->hw.semaphores[submit_info.waitSemaphoreCount] = vk->context->swapchain_wait_semaphores[frame_index];
vk->hw.wait_dst_stages[submit_info.waitSemaphoreCount] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
submit_info.waitSemaphoreCount++;
}
}
else if (vk->context->has_acquired_swapchain &&
vk->context->swapchain_acquire_semaphore != VK_NULL_HANDLE)
{
static const VkPipelineStageFlags wait_stage =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
assert(!vk->context->swapchain_wait_semaphores[frame_index]);
vk->context->swapchain_wait_semaphores[frame_index] =
vk->context->swapchain_acquire_semaphore;
vk->context->swapchain_acquire_semaphore = VK_NULL_HANDLE;
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &vk->context->swapchain_wait_semaphores[frame_index];
submit_info.pWaitDstStageMask = &wait_stage;
} }
submit_info.signalSemaphoreCount = 0; submit_info.signalSemaphoreCount = 0;
if (vk->context->swapchain_semaphores[frame_index] != VK_NULL_HANDLE && if (vk->context->swapchain_semaphores[swapchain_index] != VK_NULL_HANDLE &&
vk->context->has_acquired_swapchain) vk->context->has_acquired_swapchain)
{ {
signal_semaphores[submit_info.signalSemaphoreCount++] = vk->context->swapchain_semaphores[frame_index]; signal_semaphores[submit_info.signalSemaphoreCount++] = vk->context->swapchain_semaphores[swapchain_index];
} }
if (vk->hw.signal_semaphore != VK_NULL_HANDLE) if (vk->hw.signal_semaphore != VK_NULL_HANDLE)
@ -2142,7 +2201,7 @@ static bool vulkan_frame(void *data, const void *frame,
/* Disable BFI during fast forward, slow-motion, /* Disable BFI during fast forward, slow-motion,
* and pause to prevent flicker. */ * and pause to prevent flicker. */
if ( if (
chain->backbuffer.image != VK_NULL_HANDLE backbuffer->image != VK_NULL_HANDLE
&& vk->context->has_acquired_swapchain && vk->context->has_acquired_swapchain
&& black_frame_insertion && black_frame_insertion
&& !input_driver_nonblock_state && !input_driver_nonblock_state
@ -2212,7 +2271,7 @@ static bool vulkan_get_current_sw_framebuffer(void *data,
struct vk_per_frame *chain = NULL; struct vk_per_frame *chain = NULL;
vk_t *vk = (vk_t*)data; vk_t *vk = (vk_t*)data;
vk->chain = vk->chain =
&vk->swapchain[vk->context->current_swapchain_index]; &vk->swapchain[vk->context->current_frame_index];
chain = vk->chain; chain = vk->chain;
if (chain->texture.width != framebuffer->width || if (chain->texture.width != framebuffer->width ||
@ -2282,7 +2341,7 @@ static void vulkan_set_texture_frame(void *data,
if (!vk) if (!vk)
return; return;
index = vk->context->current_swapchain_index; index = vk->context->current_frame_index;
texture = &vk->menu.textures[index]; texture = &vk->menu.textures[index];
texture_optimal = &vk->menu.textures_optimal[index]; texture_optimal = &vk->menu.textures_optimal[index];
@ -2486,7 +2545,7 @@ static bool vulkan_read_viewport(void *data, uint8_t *buffer, bool is_idle)
if (!vk) if (!vk)
return false; return false;
staging = &vk->readback.staging[vk->context->current_swapchain_index]; staging = &vk->readback.staging[vk->context->current_frame_index];
if (vk->readback.streamed) if (vk->readback.streamed)
{ {