vulkan/wsi: Add mechanism to wait for WSI semaphore unsignal.

When vkWaitForPresentKHR succeeds, we are guaranteed
that any dependent semaphores have been unsignalled.

In an explicit sync world, we are guaranteed this automatically by
having a present complete, since that event must follow a semaphore wait
completion.

However, if the swapchain image is implicitly
synchronized, the semaphore might technically not have been unsignaled
before the present complete event triggers.

Present IDs must be signalled in monotonic order, same as timeline
semaphores.

Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no>
Reviewed-by: Joshua Ashton <joshua@froggi.es>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/19279>
This commit is contained in:
Hans-Kristian Arntzen 2022-10-24 14:47:51 +02:00 committed by Marge Bot
parent 339c0f6a7e
commit c888da3d48
3 changed files with 85 additions and 0 deletions

View file

@ -129,6 +129,12 @@ wsi_device_init(struct wsi_device *wsi,
&vk_physical_device_from_handle(pdevice)->supported_extensions;
wsi->has_import_memory_host =
supported_extensions->EXT_external_memory_host;
wsi->khr_present_wait =
supported_extensions->KHR_present_id &&
supported_extensions->KHR_present_wait;
/* We cannot expose KHR_present_wait without timeline semaphores. */
assert(!wsi->khr_present_wait || supported_extensions->KHR_timeline_semaphore);
list_inithead(&wsi->hotplug_fences);
@ -169,6 +175,8 @@ wsi_device_init(struct wsi_device *wsi,
WSI_GET_CB(WaitForFences);
WSI_GET_CB(MapMemory);
WSI_GET_CB(UnmapMemory);
if (wsi->khr_present_wait)
WSI_GET_CB(WaitSemaphoresKHR);
#undef WSI_GET_CB
#ifdef VK_USE_PLATFORM_XCB_KHR
@ -487,6 +495,8 @@ wsi_swapchain_finish(struct wsi_swapchain *chain)
}
chain->wsi->DestroySemaphore(chain->device, chain->dma_buf_semaphore,
&chain->alloc);
chain->wsi->DestroySemaphore(chain->device, chain->present_id_timeline,
&chain->alloc);
int cmd_pools_count = chain->buffer_blit_queue != VK_NULL_HANDLE ?
1 : chain->wsi->queue_family_count;
@ -879,12 +889,33 @@ wsi_CreateSwapchainKHR(VkDevice _device,
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
if (wsi_device->khr_present_wait) {
const VkSemaphoreTypeCreateInfo type_info = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE,
};
const VkSemaphoreCreateInfo sem_info = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = &type_info,
.flags = 0,
};
/* We assume here that a driver exposing present_wait also exposes VK_KHR_timeline_semaphore. */
result = wsi_device->CreateSemaphore(_device, &sem_info, alloc, &swapchain->present_id_timeline);
if (result != VK_SUCCESS) {
swapchain->destroy(swapchain, alloc);
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
}
if (swapchain->buffer_blit_queue != VK_NULL_HANDLE) {
swapchain->buffer_blit_semaphores = vk_zalloc(alloc,
sizeof (*swapchain->buffer_blit_semaphores) * swapchain->image_count,
sizeof (*swapchain->buffer_blit_semaphores),
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (!swapchain->buffer_blit_semaphores) {
wsi_device->DestroySemaphore(_device, swapchain->present_id_timeline, alloc);
swapchain->destroy(swapchain, alloc);
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
@ -1091,6 +1122,27 @@ wsi_AcquireNextImage2KHR(VkDevice _device,
_device, pAcquireInfo, pImageIndex);
}
static VkResult wsi_signal_present_id_timeline(struct wsi_swapchain *swapchain,
VkQueue queue, uint64_t present_id)
{
assert(swapchain->present_id_timeline);
const VkTimelineSemaphoreSubmitInfo timeline_info = {
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
.pSignalSemaphoreValues = &present_id,
.signalSemaphoreValueCount = 1,
};
const VkSubmitInfo submit_info = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = &timeline_info,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &swapchain->present_id_timeline,
};
return swapchain->wsi->QueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
}
VkResult
wsi_common_queue_present(const struct wsi_device *wsi,
VkDevice device,
@ -1260,6 +1312,12 @@ wsi_common_queue_present(const struct wsi_device *wsi,
if (present_ids && present_ids->pPresentIds)
present_id = present_ids->pPresentIds[i];
if (present_id) {
result = wsi_signal_present_id_timeline(swapchain, queue, present_id);
if (result != VK_SUCCESS)
goto fail_present;
}
result = swapchain->queue_present(swapchain, image_index, present_id, region);
if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR)
goto fail_present;
@ -1386,6 +1444,21 @@ wsi_common_bind_swapchain_image(const struct wsi_device *wsi,
return wsi->BindImageMemory(chain->device, vk_image, image->memory, 0);
}
VkResult
wsi_swapchain_wait_for_present_semaphore(const struct wsi_swapchain *chain,
uint64_t present_id, uint64_t timeout)
{
assert(chain->present_id_timeline);
const VkSemaphoreWaitInfo wait_info = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO,
.semaphoreCount = 1,
.pSemaphores = &chain->present_id_timeline,
.pValues = &present_id,
};
return chain->wsi->WaitSemaphoresKHR(chain->device, &wait_info, timeout);
}
uint32_t
wsi_select_memory_type(const struct wsi_device *wsi,
VkMemoryPropertyFlags req_props,

View file

@ -166,6 +166,12 @@ struct wsi_device {
*/
bool signal_fence_with_memory;
/* Whether present_wait functionality is enabled on the device.
* In this case, we have to create an extra timeline semaphore
* to be able to synchronize with the WSI present semaphore being unsignalled.
* This requires VK_KHR_timeline_semaphore. */
bool khr_present_wait;
/*
* This sets the ownership for a WSI memory object:
*
@ -229,6 +235,7 @@ struct wsi_device {
WSI_CB(WaitForFences);
WSI_CB(MapMemory);
WSI_CB(UnmapMemory);
WSI_CB(WaitSemaphoresKHR);
#undef WSI_CB
struct wsi_interface * wsi[VK_ICD_WSI_PLATFORM_MAX];

View file

@ -134,6 +134,7 @@ struct wsi_swapchain {
VkFence* fences;
VkSemaphore* buffer_blit_semaphores;
VkPresentModeKHR present_mode;
VkSemaphore present_id_timeline;
int signal_dma_buf_from_semaphore;
VkSemaphore dma_buf_semaphore;
@ -256,6 +257,10 @@ void
wsi_destroy_image(const struct wsi_swapchain *chain,
struct wsi_image *image);
VkResult
wsi_swapchain_wait_for_present_semaphore(const struct wsi_swapchain *chain,
uint64_t present_id, uint64_t timeout);
#ifdef HAVE_LIBDRM
VkResult
wsi_prepare_signal_dma_buf_from_semaphore(struct wsi_swapchain *chain,