diff --git a/gfx/common/vulkan_common.c b/gfx/common/vulkan_common.c index d3072d9359..7f05ecae7a 100644 --- a/gfx/common/vulkan_common.c +++ b/gfx/common/vulkan_common.c @@ -42,7 +42,7 @@ #define VENDOR_ID_NV 0x10DE #define VENDOR_ID_INTEL 0x8086 -#if defined(_WIN32) || defined(ANDROID) +#if defined(_WIN32) #define VULKAN_EMULATE_MAILBOX #endif @@ -1675,10 +1675,6 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk) "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*)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(), device_extensions, ARRAY_SIZE(device_extensions), -#ifdef VULKAN_DEBUG - device_layers, - ARRAY_SIZE(device_layers), -#else NULL, 0, -#endif &features); if (!ret) @@ -1755,6 +1746,22 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk) vk->emulate_mailbox = vk->fullscreen; #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); { @@ -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.ppEnabledExtensionNames = enabled_device_extension_count ? enabled_device_extensions : NULL; device_info.pEnabledFeatures = &features; -#ifdef VULKAN_DEBUG - device_info.enabledLayerCount = ARRAY_SIZE(device_layers); - device_info.ppEnabledLayerNames = device_layers; -#endif if (cached_device_vk) { @@ -1917,7 +1920,7 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk, #ifdef VULKAN_DEBUG 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 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) vkDestroyFence(vk->context.device, 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)); - memset(vk->context.swapchain_fences, 0, sizeof(vk->context.swapchain_fences)); + if (vk->context.swapchain_acquire_semaphore != VK_NULL_HANDLE) + 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) @@ -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) { 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_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) @@ -2706,7 +2755,12 @@ 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; + /* 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]; if (*next_fence != VK_NULL_HANDLE) @@ -2718,6 +2772,10 @@ static void vulkan_acquire_wait_fences(gfx_ctx_vulkan_data_t *vk) else vkCreateFence(vk->context.device, &fence_info, NULL, next_fence); 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) @@ -2732,13 +2790,16 @@ static void vulkan_create_wait_fences(gfx_ctx_vulkan_data_t *vk) vkCreateFence(vk->context.device, &fence_info, NULL, &vk->context.swapchain_fences[i]); } + + vk->context.current_frame_index = 0; } void vulkan_acquire_next_image(gfx_ctx_vulkan_data_t *vk) { unsigned index; VkResult err; - VkFence fence; + VkFence fence = VK_NULL_HANDLE; + VkSemaphore semaphore = VK_NULL_HANDLE; VkFenceCreateInfo fence_info = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; VkSemaphoreCreateInfo sem_info = @@ -2760,6 +2821,7 @@ retry: { /* We still don't have a swapchain, so just fake it ... */ vk->context.current_swapchain_index = 0; + vk->context.current_frame_index = 0; vulkan_acquire_clear_fences(vk); vulkan_acquire_wait_fences(vk); vk->context.invalid_swapchain = true; @@ -2776,31 +2838,54 @@ retry: * MAILBOX would do. */ err = vulkan_emulated_mailbox_acquire_next_image( &vk->mailbox, &vk->context.current_swapchain_index); - fence = VK_NULL_HANDLE; } else { - vkCreateFence(vk->context.device, &fence_info, NULL, &fence); + if (vk->use_wsi_semaphore) + semaphore = vulkan_get_wsi_acquire_semaphore(&vk->context); + else + 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); + semaphore, fence, &vk->context.current_swapchain_index); +#ifdef ANDROID /* VK_SUBOPTIMAL_KHR can be returned on Android 10 * when prerotate is not dealt with. * This is not an error we need to care about, and * we'll treat it as SUCCESS. */ if (err == VK_SUBOPTIMAL_KHR) err = VK_SUCCESS; +#endif } - if (err == VK_SUCCESS) + if (err == VK_SUCCESS || err == VK_SUBOPTIMAL_KHR) { if (fence != VK_NULL_HANDLE) vkWaitForFences(vk->context.device, 1, &fence, true, UINT64_MAX); 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 + { vk->context.has_acquired_swapchain = false; + if (semaphore) + vulkan_recycle_acquire_semaphore(&vk->context, semaphore); + } #ifdef WSI_HARDENING_TEST trigger_spurious_error_vkresult(&err); @@ -2811,11 +2896,9 @@ retry: 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; + /* Do nothing. */ } - 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. */ vulkan_destroy_swapchain(vk); diff --git a/gfx/common/vulkan_common.h b/gfx/common/vulkan_common.h index 7fa4813989..475d6afb7a 100644 --- a/gfx/common/vulkan_common.h +++ b/gfx/common/vulkan_common.h @@ -102,6 +102,7 @@ typedef struct vulkan_context uint32_t graphics_queue_index; uint32_t num_swapchain_images; uint32_t current_swapchain_index; + uint32_t current_frame_index; VkInstance instance; VkPhysicalDevice gpu; @@ -117,6 +118,11 @@ typedef struct vulkan_context VkSemaphore swapchain_semaphores[VULKAN_MAX_SWAPCHAIN_IMAGES]; 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; retro_vulkan_destroy_device_t destroy_device; @@ -153,6 +159,10 @@ typedef struct gfx_ctx_vulkan_data bool created_new_swapchain; bool emulate_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; VkSurfaceKHR vk_surface; VkSwapchainKHR swapchain; @@ -285,7 +295,6 @@ struct vk_descriptor_manager struct vk_per_frame { - struct vk_image backbuffer; struct vk_texture texture; struct vk_texture texture_optimal; struct vk_buffer_chain vbo; @@ -344,7 +353,9 @@ typedef struct vk VkRenderPass render_pass; struct video_viewport vp; struct vk_per_frame *chain; + struct vk_image *backbuffer; struct vk_per_frame swapchain[VULKAN_MAX_SWAPCHAIN_IMAGES]; + struct vk_image backbuffers[VULKAN_MAX_SWAPCHAIN_IMAGES]; struct vk_texture default_texture; /* Currently active command buffer. */ @@ -419,7 +430,7 @@ typedef struct vk struct retro_hw_render_interface_vulkan iface; const struct retro_vulkan_image *image; - const VkSemaphore *semaphores; + VkSemaphore *semaphores; VkSemaphore signal_semaphore; VkPipelineStageFlags *wait_dst_stages; VkCommandBuffer *cmd; diff --git a/gfx/drivers/vulkan.c b/gfx/drivers/vulkan.c index 39a49d38f5..db07f7e311 100644 --- a/gfx/drivers/vulkan.c +++ b/gfx/drivers/vulkan.c @@ -136,19 +136,19 @@ static void vulkan_init_framebuffers( VkFramebufferCreateInfo 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) { - vk->swapchain[i].backbuffer.view = VK_NULL_HANDLE; - vk->swapchain[i].backbuffer.framebuffer = VK_NULL_HANDLE; + vk->backbuffers[i].view = VK_NULL_HANDLE; + vk->backbuffers[i].framebuffer = VK_NULL_HANDLE; continue; } /* Create an image view which we can render into. */ view.viewType = VK_IMAGE_VIEW_TYPE_2D; 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.baseArrayLayer = 0; view.subresourceRange.levelCount = 1; @@ -160,18 +160,18 @@ static void vulkan_init_framebuffers( view.components.a = VK_COMPONENT_SWIZZLE_A; vkCreateImageView(vk->context->device, - &view, NULL, &vk->swapchain[i].backbuffer.view); + &view, NULL, &vk->backbuffers[i].view); /* Create the framebuffer */ info.renderPass = vk->render_pass; info.attachmentCount = 1; - info.pAttachments = &vk->swapchain[i].backbuffer.view; + info.pAttachments = &vk->backbuffers[i].view; info.width = vk->context->swapchain_width; info.height = vk->context->swapchain_height; info.layers = 1; 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; for (i = 0; i < vk->num_swapchain_images; i++) { - if (vk->swapchain[i].backbuffer.framebuffer) + if (vk->backbuffers[i].framebuffer) { 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, - 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.pipeline_cache = vk->pipelines.cache; 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.height = vk->tex_h; 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.pipeline_cache = vk->pipelines.cache; 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.height = vk->tex_h; info.swapchain.viewport = vk->vk_vp; @@ -909,6 +909,7 @@ static void vulkan_deinit_static_resources(vk_t *vk) vk->staging_pool, NULL); free(vk->hw.cmd); free(vk->hw.wait_dst_stages); + free(vk->hw.semaphores); for (i = 0; i < VULKAN_MAX_SWAPCHAIN_IMAGES; i++) 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) { 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) @@ -1002,21 +1003,27 @@ static void vulkan_set_image(void *handle, vk->hw.image = image; vk->hw.num_semaphores = num_semaphores; - vk->hw.semaphores = semaphores; if (num_semaphores > 0) { - VkPipelineStageFlags *stage_flags = (VkPipelineStageFlags*) - realloc(vk->hw.wait_dst_stages, - sizeof(VkPipelineStageFlags) * vk->hw.num_semaphores); + /* Allocate one extra in case we need to use WSI acquire semaphores. */ + VkPipelineStageFlags *stage_flags = (VkPipelineStageFlags*)realloc(vk->hw.wait_dst_stages, + 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. */ - retro_assert(stage_flags); + retro_assert(stage_flags && new_semaphores); vk->hw.wait_dst_stages = stage_flags; + vk->hw.semaphores = new_semaphores; for (i = 0; i < vk->hw.num_semaphores; i++) + { 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.src_queue_family = src_queue_family; @@ -1585,14 +1592,14 @@ static void vulkan_readback(vk_t *vk) region.imageExtent.height = vp.height; 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->memory != VK_NULL_HANDLE ? staging : NULL, vk->vp.width, vk->vp.height, VK_FORMAT_B8G8R8A8_UNORM, /* Formats don't matter for readback since it's a raw copy. */ NULL, NULL, VULKAN_TEXTURE_READBACK); - vkCmdCopyImageToBuffer(vk->cmd, vk->chain->backbuffer.image, + vkCmdCopyImageToBuffer(vk->cmd, vk->backbuffer->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, staging->buffer, 1, ®ion); @@ -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 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_image *backbuffer = &vk->backbuffers[swapchain_index]; vk->chain = chain; vk->cmd = chain->cmd; begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; vkResetCommandBuffer(vk->cmd, 0); 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, 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); - 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); - 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_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_MEMORY_READ_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.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.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 @@ -1671,6 +1696,7 @@ static bool vulkan_frame(void *data, const void *frame, VkSemaphore signal_semaphores[2]; vk_t *vk = (vk_t*)data; struct vk_per_frame *chain = NULL; + struct vk_image *backbuffer = NULL; bool waits_for_semaphores = false; unsigned width = video_info->width; unsigned height = video_info->height; @@ -1694,11 +1720,15 @@ static bool vulkan_frame(void *data, const void *frame, VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; unsigned frame_index = + vk->context->current_frame_index; + unsigned swapchain_index = vk->context->current_swapchain_index; /* Bookkeeping on start of frame. */ - chain = &vk->swapchain[frame_index]; - vk->chain = chain; + chain = &vk->swapchain[frame_index]; + backbuffer = &vk->backbuffers[swapchain_index]; + vk->chain = chain; + vk->backbuffer = backbuffer; { struct vk_descriptor_manager *manager = &chain->descriptor_manager; @@ -1901,10 +1931,10 @@ static bool vulkan_frame(void *data, const void *frame, #endif /* 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.framebuffer = chain->backbuffer.framebuffer; + rp_info.framebuffer = backbuffer->framebuffer; rp_info.renderArea.extent.width = vk->context->swapchain_width; rp_info.renderArea.extent.height = vk->context->swapchain_height; 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[3] = 0.0f; - /* Prepare backbuffer for rendering. We don't use WSI semaphores here. */ - vulkan_image_layout_transition(vk, vk->cmd, chain->backbuffer.image, + /* Prepare backbuffer for rendering. */ + vulkan_image_layout_transition(vk, vk->cmd, 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, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); /* 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); - if (chain->backbuffer.image != VK_NULL_HANDLE && + if (backbuffer->image != VK_NULL_HANDLE && vk->context->has_acquired_swapchain && (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. */ vulkan_image_layout_transition(vk, - vk->cmd, chain->backbuffer.image, + vk->cmd, backbuffer->image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, - VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); vulkan_readback(vk); /* Prepare for presentation after transfers are complete. */ vulkan_image_layout_transition(vk, vk->cmd, - chain->backbuffer.image, + backbuffer->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, 0, @@ -2033,17 +2063,17 @@ static bool vulkan_frame(void *data, const void *frame, vk->readback.pending = false; } - else if (chain->backbuffer.image != VK_NULL_HANDLE && + else if (backbuffer->image != VK_NULL_HANDLE && vk->context->has_acquired_swapchain) { /* Prepare backbuffer for presentation. */ vulkan_image_layout_transition(vk, vk->cmd, - chain->backbuffer.image, + backbuffer->image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_ACCESS_MEMORY_READ_BIT, - VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + 0, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); } @@ -2090,14 +2120,43 @@ static bool vulkan_frame(void *data, const void *frame, /* Consume the semaphores. */ 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; - 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) { - 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) @@ -2142,7 +2201,7 @@ static bool vulkan_frame(void *data, const void *frame, /* Disable BFI during fast forward, slow-motion, * and pause to prevent flicker. */ if ( - chain->backbuffer.image != VK_NULL_HANDLE + backbuffer->image != VK_NULL_HANDLE && vk->context->has_acquired_swapchain && black_frame_insertion && !input_driver_nonblock_state @@ -2212,7 +2271,7 @@ static bool vulkan_get_current_sw_framebuffer(void *data, struct vk_per_frame *chain = NULL; vk_t *vk = (vk_t*)data; vk->chain = - &vk->swapchain[vk->context->current_swapchain_index]; + &vk->swapchain[vk->context->current_frame_index]; chain = vk->chain; if (chain->texture.width != framebuffer->width || @@ -2282,7 +2341,7 @@ static void vulkan_set_texture_frame(void *data, if (!vk) return; - index = vk->context->current_swapchain_index; + index = vk->context->current_frame_index; texture = &vk->menu.textures[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) return false; - staging = &vk->readback.staging[vk->context->current_swapchain_index]; + staging = &vk->readback.staging[vk->context->current_frame_index]; if (vk->readback.streamed) {