mirror of
https://github.com/hyprwm/wlroots-hyprland.git
synced 2024-11-25 14:15:58 +01:00
render/vulkan: switch to timeline semaphores
Up until now we were using a VkFence for GPU-to-CPU synchronization. This has some limitations that become a blocker when trying to have multiple command buffers in flight at once (e.g. for multi-output). It's desirable to implement a command buffer pool [1], but VkFence cannot be used to track command buffer completion for individual subpasses. Let's just switch to timeline semaphores [2], which fix this issue, make synchronization a lot more ergonomic and are a core Vulkan 1.2 feature. [1]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3802 [2]: https://www.khronos.org/blog/vulkan-timeline-semaphores
This commit is contained in:
parent
f92d1499cd
commit
a8a194d695
3 changed files with 62 additions and 27 deletions
|
@ -44,6 +44,7 @@ struct wlr_vk_device {
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
PFN_vkGetMemoryFdPropertiesKHR getMemoryFdPropertiesKHR;
|
PFN_vkGetMemoryFdPropertiesKHR getMemoryFdPropertiesKHR;
|
||||||
|
PFN_vkWaitSemaphoresKHR waitSemaphoresKHR;
|
||||||
} api;
|
} api;
|
||||||
|
|
||||||
uint32_t format_prop_count;
|
uint32_t format_prop_count;
|
||||||
|
@ -151,7 +152,8 @@ struct wlr_vk_renderer {
|
||||||
VkPipelineLayout pipe_layout;
|
VkPipelineLayout pipe_layout;
|
||||||
VkSampler sampler;
|
VkSampler sampler;
|
||||||
|
|
||||||
VkFence fence;
|
VkSemaphore timeline_semaphore;
|
||||||
|
uint64_t timeline_point;
|
||||||
|
|
||||||
struct wlr_vk_render_buffer *current_render_buffer;
|
struct wlr_vk_render_buffer *current_render_buffer;
|
||||||
|
|
||||||
|
|
|
@ -349,32 +349,41 @@ bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer) {
|
||||||
vkEndCommandBuffer(renderer->stage.cb);
|
vkEndCommandBuffer(renderer->stage.cb);
|
||||||
renderer->stage.recording = false;
|
renderer->stage.recording = false;
|
||||||
|
|
||||||
|
renderer->timeline_point++;
|
||||||
|
|
||||||
|
VkTimelineSemaphoreSubmitInfoKHR timeline_submit_info = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR,
|
||||||
|
.signalSemaphoreValueCount = 1,
|
||||||
|
.pSignalSemaphoreValues = &renderer->timeline_point,
|
||||||
|
};
|
||||||
VkSubmitInfo submit_info = {
|
VkSubmitInfo submit_info = {
|
||||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||||
.commandBufferCount = 1u,
|
.pNext = &timeline_submit_info,
|
||||||
|
.commandBufferCount = 1,
|
||||||
.pCommandBuffers = &renderer->stage.cb,
|
.pCommandBuffers = &renderer->stage.cb,
|
||||||
|
.signalSemaphoreCount = 1,
|
||||||
|
.pSignalSemaphores = &renderer->timeline_semaphore,
|
||||||
};
|
};
|
||||||
VkResult res = vkQueueSubmit(renderer->dev->queue, 1,
|
VkResult res = vkQueueSubmit(renderer->dev->queue, 1, &submit_info, NULL);
|
||||||
&submit_info, renderer->fence);
|
|
||||||
if (res != VK_SUCCESS) {
|
if (res != VK_SUCCESS) {
|
||||||
wlr_vk_error("vkQueueSubmit", res);
|
wlr_vk_error("vkQueueSubmit", res);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = vkWaitForFences(renderer->dev->dev, 1, &renderer->fence, true,
|
VkSemaphoreWaitInfoKHR wait_info = {
|
||||||
UINT64_MAX);
|
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO_KHR,
|
||||||
|
.semaphoreCount = 1,
|
||||||
|
.pSemaphores = &renderer->timeline_semaphore,
|
||||||
|
.pValues = &renderer->timeline_point,
|
||||||
|
};
|
||||||
|
res = renderer->dev->api.waitSemaphoresKHR(renderer->dev->dev, &wait_info, UINT64_MAX);
|
||||||
if (res != VK_SUCCESS) {
|
if (res != VK_SUCCESS) {
|
||||||
wlr_vk_error("vkWaitForFences", res);
|
wlr_vk_error("vkWaitSemaphoresKHR", res);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: don't release stage allocations here since they may still be
|
// NOTE: don't release stage allocations here since they may still be
|
||||||
// used for reading. Will be done next frame.
|
// used for reading. Will be done next frame.
|
||||||
res = vkResetFences(renderer->dev->dev, 1, &renderer->fence);
|
|
||||||
if (res != VK_SUCCESS) {
|
|
||||||
wlr_vk_error("vkResetFences", res);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -726,14 +735,25 @@ static void vulkan_end(struct wlr_renderer *wlr_renderer) {
|
||||||
++submit_count;
|
++submit_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderer->timeline_point++;
|
||||||
|
|
||||||
|
VkTimelineSemaphoreSubmitInfoKHR timeline_submit_info = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR,
|
||||||
|
.signalSemaphoreValueCount = 1,
|
||||||
|
.pSignalSemaphoreValues = &renderer->timeline_point,
|
||||||
|
};
|
||||||
|
|
||||||
VkSubmitInfo *render_sub = &submit_infos[submit_count];
|
VkSubmitInfo *render_sub = &submit_infos[submit_count];
|
||||||
render_sub->sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
render_sub->sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||||
|
render_sub->pNext = &timeline_submit_info;
|
||||||
render_sub->pCommandBuffers = &render_cb;
|
render_sub->pCommandBuffers = &render_cb;
|
||||||
render_sub->commandBufferCount = 1u;
|
render_sub->commandBufferCount = 1u;
|
||||||
|
render_sub->signalSemaphoreCount = 1;
|
||||||
|
render_sub->pSignalSemaphores = &renderer->timeline_semaphore,
|
||||||
++submit_count;
|
++submit_count;
|
||||||
|
|
||||||
VkResult res = vkQueueSubmit(renderer->dev->queue, submit_count,
|
VkResult res = vkQueueSubmit(renderer->dev->queue, submit_count,
|
||||||
submit_infos, renderer->fence);
|
submit_infos, NULL);
|
||||||
if (res != VK_SUCCESS) {
|
if (res != VK_SUCCESS) {
|
||||||
wlr_vk_error("vkQueueSubmit", res);
|
wlr_vk_error("vkQueueSubmit", res);
|
||||||
return;
|
return;
|
||||||
|
@ -742,10 +762,15 @@ static void vulkan_end(struct wlr_renderer *wlr_renderer) {
|
||||||
// sadly this is required due to the current api/rendering model of wlr
|
// sadly this is required due to the current api/rendering model of wlr
|
||||||
// ideally we could use gpu and cpu in parallel (_without_ the
|
// ideally we could use gpu and cpu in parallel (_without_ the
|
||||||
// implicit synchronization overhead and mess of opengl drivers)
|
// implicit synchronization overhead and mess of opengl drivers)
|
||||||
res = vkWaitForFences(renderer->dev->dev, 1, &renderer->fence, true,
|
VkSemaphoreWaitInfoKHR wait_info = {
|
||||||
UINT64_MAX);
|
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO_KHR,
|
||||||
|
.semaphoreCount = 1,
|
||||||
|
.pSemaphores = &renderer->timeline_semaphore,
|
||||||
|
.pValues = &renderer->timeline_point,
|
||||||
|
};
|
||||||
|
res = renderer->dev->api.waitSemaphoresKHR(renderer->dev->dev, &wait_info, UINT64_MAX);
|
||||||
if (res != VK_SUCCESS) {
|
if (res != VK_SUCCESS) {
|
||||||
wlr_vk_error("vkWaitForFences", res);
|
wlr_vk_error("vkWaitSemaphoresKHR", res);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -758,11 +783,6 @@ static void vulkan_end(struct wlr_renderer *wlr_renderer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wl_list_init(&renderer->destroy_textures); // reset the list
|
wl_list_init(&renderer->destroy_textures); // reset the list
|
||||||
res = vkResetFences(renderer->dev->dev, 1, &renderer->fence);
|
|
||||||
if (res != VK_SUCCESS) {
|
|
||||||
wlr_vk_error("vkResetFences", res);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool vulkan_render_subtexture_with_matrix(struct wlr_renderer *wlr_renderer,
|
static bool vulkan_render_subtexture_with_matrix(struct wlr_renderer *wlr_renderer,
|
||||||
|
@ -979,7 +999,7 @@ static void vulkan_destroy(struct wlr_renderer *wlr_renderer) {
|
||||||
vkDestroyShaderModule(dev->dev, renderer->tex_frag_module, NULL);
|
vkDestroyShaderModule(dev->dev, renderer->tex_frag_module, NULL);
|
||||||
vkDestroyShaderModule(dev->dev, renderer->quad_frag_module, NULL);
|
vkDestroyShaderModule(dev->dev, renderer->quad_frag_module, NULL);
|
||||||
|
|
||||||
vkDestroyFence(dev->dev, renderer->fence, NULL);
|
vkDestroySemaphore(dev->dev, renderer->timeline_semaphore, NULL);
|
||||||
vkDestroyPipelineLayout(dev->dev, renderer->pipe_layout, NULL);
|
vkDestroyPipelineLayout(dev->dev, renderer->pipe_layout, NULL);
|
||||||
vkDestroyDescriptorSetLayout(dev->dev, renderer->ds_layout, NULL);
|
vkDestroyDescriptorSetLayout(dev->dev, renderer->ds_layout, NULL);
|
||||||
vkDestroySampler(dev->dev, renderer->sampler, NULL);
|
vkDestroySampler(dev->dev, renderer->sampler, NULL);
|
||||||
|
@ -1745,13 +1765,19 @@ struct wlr_renderer *vulkan_renderer_create_for_device(struct wlr_vk_device *dev
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkFenceCreateInfo fence_info = {
|
VkSemaphoreTypeCreateInfoKHR semaphore_type_info = {
|
||||||
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR,
|
||||||
|
.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE_KHR,
|
||||||
|
.initialValue = 0,
|
||||||
};
|
};
|
||||||
res = vkCreateFence(dev->dev, &fence_info, NULL,
|
VkSemaphoreCreateInfo semaphore_info = {
|
||||||
&renderer->fence);
|
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||||
|
.pNext = &semaphore_type_info,
|
||||||
|
};
|
||||||
|
res = vkCreateSemaphore(dev->dev, &semaphore_info, NULL,
|
||||||
|
&renderer->timeline_semaphore);
|
||||||
if (res != VK_SUCCESS) {
|
if (res != VK_SUCCESS) {
|
||||||
wlr_vk_error("vkCreateFence", res);
|
wlr_vk_error("vkCreateSemaphore", res);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -424,6 +424,7 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini,
|
||||||
VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME,
|
VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME,
|
||||||
VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME,
|
VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME,
|
||||||
VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME,
|
VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME,
|
||||||
|
VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME, // or vulkan 1.2
|
||||||
};
|
};
|
||||||
size_t extensions_len = sizeof(extensions) / sizeof(extensions[0]);
|
size_t extensions_len = sizeof(extensions) / sizeof(extensions[0]);
|
||||||
|
|
||||||
|
@ -464,8 +465,13 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini,
|
||||||
.pQueuePriorities = &prio,
|
.pQueuePriorities = &prio,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
VkPhysicalDeviceTimelineSemaphoreFeaturesKHR timeline_features = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR,
|
||||||
|
.timelineSemaphore = VK_TRUE,
|
||||||
|
};
|
||||||
VkDeviceCreateInfo dev_info = {
|
VkDeviceCreateInfo dev_info = {
|
||||||
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||||
|
.pNext = &timeline_features,
|
||||||
.queueCreateInfoCount = 1u,
|
.queueCreateInfoCount = 1u,
|
||||||
.pQueueCreateInfos = &qinfo,
|
.pQueueCreateInfos = &qinfo,
|
||||||
.enabledExtensionCount = extensions_len,
|
.enabledExtensionCount = extensions_len,
|
||||||
|
@ -482,6 +488,7 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini,
|
||||||
|
|
||||||
load_device_proc(dev, "vkGetMemoryFdPropertiesKHR",
|
load_device_proc(dev, "vkGetMemoryFdPropertiesKHR",
|
||||||
&dev->api.getMemoryFdPropertiesKHR);
|
&dev->api.getMemoryFdPropertiesKHR);
|
||||||
|
load_device_proc(dev, "vkWaitSemaphoresKHR", &dev->api.waitSemaphoresKHR);
|
||||||
|
|
||||||
// - check device format support -
|
// - check device format support -
|
||||||
size_t max_fmts;
|
size_t max_fmts;
|
||||||
|
|
Loading…
Reference in a new issue