Vulkan: Swapchain synchronization
This PR adds swapchain synchronization. When the swapchain swaps the buffers it can add a wait semaphore/signal semaphore to support GPU based synchronization 10 times playback of `rain_restaurant.blend` on AMD RX 7700 Before: 10 × Animation playback: 72347.5540 ms, average: 7234.75539684 ms After: 10 × Animation playback: 41523.2441 ms, average: 4152.32441425 ms Getting around the OpenGL performance target. Pull Request: https://projects.blender.org/blender/blender/pulls/136259
This commit is contained in:
@@ -746,6 +746,10 @@ typedef struct {
|
||||
VkSurfaceFormatKHR surface_format;
|
||||
/** Resolution of the image. */
|
||||
VkExtent2D extent;
|
||||
/** Semaphore to wait before updating the image. */
|
||||
VkSemaphore acquire_semaphore;
|
||||
/** Semaphore to signal after the image has been updated. */
|
||||
VkSemaphore present_semaphore;
|
||||
} GHOST_VulkanSwapChainData;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -485,7 +485,7 @@ GHOST_ContextVK::GHOST_ContextVK(bool stereoVisual,
|
||||
m_command_buffer(VK_NULL_HANDLE),
|
||||
m_surface(VK_NULL_HANDLE),
|
||||
m_swapchain(VK_NULL_HANDLE),
|
||||
m_fence(VK_NULL_HANDLE)
|
||||
m_render_frame(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -523,10 +523,16 @@ GHOST_TSuccess GHOST_ContextVK::destroySwapchain()
|
||||
if (m_swapchain != VK_NULL_HANDLE) {
|
||||
vkDestroySwapchainKHR(device, m_swapchain, nullptr);
|
||||
}
|
||||
if (m_fence != VK_NULL_HANDLE) {
|
||||
vkDestroyFence(device, m_fence, nullptr);
|
||||
m_fence = VK_NULL_HANDLE;
|
||||
VK_CHECK(vkDeviceWaitIdle(device));
|
||||
for (VkSemaphore semaphore : m_acquire_semaphores) {
|
||||
vkDestroySemaphore(device, semaphore, nullptr);
|
||||
}
|
||||
m_acquire_semaphores.clear();
|
||||
for (VkSemaphore semaphore : m_present_semaphores) {
|
||||
vkDestroySemaphore(device, semaphore, nullptr);
|
||||
}
|
||||
m_present_semaphores.clear();
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
@@ -562,21 +568,27 @@ GHOST_TSuccess GHOST_ContextVK::swapBuffers()
|
||||
* swapchain image. Other do it when calling vkQueuePresent. */
|
||||
VkResult result = VK_ERROR_OUT_OF_DATE_KHR;
|
||||
uint32_t image_index = 0;
|
||||
int32_t render_frame = 0;
|
||||
while (result == VK_ERROR_OUT_OF_DATE_KHR) {
|
||||
result = vkAcquireNextImageKHR(
|
||||
device, m_swapchain, UINT64_MAX, VK_NULL_HANDLE, m_fence, &image_index);
|
||||
render_frame = (m_render_frame + 1) % m_acquire_semaphores.size();
|
||||
result = vkAcquireNextImageKHR(device,
|
||||
m_swapchain,
|
||||
UINT64_MAX,
|
||||
m_acquire_semaphores[render_frame],
|
||||
VK_NULL_HANDLE,
|
||||
&image_index);
|
||||
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
|
||||
destroySwapchain();
|
||||
createSwapchain();
|
||||
}
|
||||
}
|
||||
VK_CHECK(vkWaitForFences(device, 1, &m_fence, VK_TRUE, UINT64_MAX));
|
||||
VK_CHECK(vkResetFences(device, 1, &m_fence));
|
||||
|
||||
GHOST_VulkanSwapChainData swap_chain_data;
|
||||
swap_chain_data.image = m_swapchain_images[image_index];
|
||||
swap_chain_data.surface_format = m_surface_format;
|
||||
swap_chain_data.extent = m_render_extent;
|
||||
swap_chain_data.acquire_semaphore = m_acquire_semaphores[render_frame];
|
||||
swap_chain_data.present_semaphore = m_present_semaphores[render_frame];
|
||||
|
||||
if (swap_buffers_pre_callback_) {
|
||||
swap_buffers_pre_callback_(&swap_chain_data);
|
||||
@@ -584,8 +596,8 @@ GHOST_TSuccess GHOST_ContextVK::swapBuffers()
|
||||
|
||||
VkPresentInfoKHR present_info = {};
|
||||
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
||||
present_info.waitSemaphoreCount = 0;
|
||||
present_info.pWaitSemaphores = nullptr;
|
||||
present_info.waitSemaphoreCount = 1;
|
||||
present_info.pWaitSemaphores = &m_present_semaphores[render_frame];
|
||||
present_info.swapchainCount = 1;
|
||||
present_info.pSwapchains = &m_swapchain;
|
||||
present_info.pImageIndices = &image_index;
|
||||
@@ -887,10 +899,17 @@ GHOST_TSuccess GHOST_ContextVK::createSwapchain()
|
||||
vkGetSwapchainImagesKHR(device, m_swapchain, &image_count, nullptr);
|
||||
m_swapchain_images.resize(image_count);
|
||||
vkGetSwapchainImagesKHR(device, m_swapchain, &image_count, m_swapchain_images.data());
|
||||
|
||||
VkFenceCreateInfo fence_info = {};
|
||||
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||||
VK_CHECK(vkCreateFence(device, &fence_info, nullptr, &m_fence));
|
||||
const VkSemaphoreCreateInfo vk_semaphore_create_info = {
|
||||
VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0};
|
||||
m_acquire_semaphores.resize(image_count);
|
||||
m_present_semaphores.resize(image_count);
|
||||
for (int index = 0; index < image_count; index++) {
|
||||
VK_CHECK(vkCreateSemaphore(
|
||||
device, &vk_semaphore_create_info, nullptr, &m_acquire_semaphores[index]));
|
||||
VK_CHECK(vkCreateSemaphore(
|
||||
device, &vk_semaphore_create_info, nullptr, &m_present_semaphores[index]));
|
||||
}
|
||||
m_render_frame = 0;
|
||||
|
||||
/* Change image layout from VK_IMAGE_LAYOUT_UNDEFINED to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR. */
|
||||
VkCommandBufferBeginInfo begin_info = {};
|
||||
|
||||
@@ -183,11 +183,13 @@ class GHOST_ContextVK : public GHOST_Context {
|
||||
VkSurfaceKHR m_surface;
|
||||
VkSwapchainKHR m_swapchain;
|
||||
std::vector<VkImage> m_swapchain_images;
|
||||
std::vector<VkSemaphore> m_acquire_semaphores;
|
||||
std::vector<VkSemaphore> m_present_semaphores;
|
||||
uint32_t m_render_frame;
|
||||
|
||||
VkExtent2D m_render_extent;
|
||||
VkExtent2D m_render_extent_min;
|
||||
VkSurfaceFormatKHR m_surface_format;
|
||||
VkFence m_fence;
|
||||
|
||||
std::function<void(const GHOST_VulkanSwapChainData *)> swap_buffers_pre_callback_;
|
||||
std::function<void(void)> swap_buffers_post_callback_;
|
||||
|
||||
Reference in New Issue
Block a user