Fix #146989: Add presentation fences

Swapchains can be destroyed safely after a new image
is presented in a new swapchain or when all submission
and presentation fences are signaled, this last ones
where missing causing issues with some drivers when
recreating swapchains.

Pull Request: https://projects.blender.org/blender/blender/pulls/147226
This commit is contained in:
Guillermo Venegas
2025-10-16 13:34:21 +02:00
committed by Jeroen Bakker
parent 31035ae19f
commit 2c4ecbd536
2 changed files with 80 additions and 1 deletions

View File

@@ -29,6 +29,7 @@
#include "CLG_log.h"
#include <algorithm>
#include <array>
#include <cassert>
#include <cstdio>
@@ -715,7 +716,10 @@ GHOST_ContextVK::~GHOST_ContextVK()
GHOST_InstanceVK &instance_vk = vulkan_instance.value();
GHOST_DeviceVK &device_vk = instance_vk.device.value();
device_vk.wait_idle();
for (VkFence fence : fence_pile_) {
vkDestroyFence(device_vk.vk_device, fence, nullptr);
}
fence_pile_.clear();
destroySwapchain();
if (surface_ != VK_NULL_HANDLE) {
@@ -760,6 +764,9 @@ GHOST_TSuccess GHOST_ContextVK::swapBufferAcquire()
if (submission_frame_data.submission_fence) {
vkWaitForFences(vk_device, 1, &submission_frame_data.submission_fence, true, UINT64_MAX);
}
for (VkSwapchainKHR swapchain : submission_frame_data.discard_pile.swapchains) {
this->destroySwapchainPresentFences(swapchain);
}
submission_frame_data.discard_pile.destroy(vk_device);
const bool use_hdr_swapchain = hdr_info_ &&
@@ -842,6 +849,42 @@ GHOST_TSuccess GHOST_ContextVK::swapBufferAcquire()
return GHOST_kSuccess;
}
VkFence GHOST_ContextVK::getFence()
{
if (!fence_pile_.empty()) {
VkFence fence = fence_pile_.back();
fence_pile_.pop_back();
return fence;
}
GHOST_DeviceVK &device_vk = vulkan_instance->device.value();
VkFence fence = VK_NULL_HANDLE;
const VkFenceCreateInfo fence_create_info = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
vkCreateFence(device_vk.vk_device, &fence_create_info, nullptr, &fence);
return fence;
}
void GHOST_ContextVK::setPresentFence(VkSwapchainKHR swapchain, VkFence present_fence)
{
if (present_fence == VK_NULL_HANDLE) {
return;
}
present_fences_[swapchain].push_back(present_fence);
GHOST_DeviceVK &device_vk = vulkan_instance->device.value();
/** Recycle signaled fences. */
for (std::pair<const VkSwapchainKHR, std::vector<VkFence>> &item : present_fences_) {
std::vector<VkFence>::iterator end = item.second.end();
std::vector<VkFence>::iterator it = std::remove_if(
item.second.begin(), item.second.end(), [&](const VkFence fence) {
if (vkGetFenceStatus(device_vk.vk_device, fence) == VK_NOT_READY) {
return false;
}
vkResetFences(device_vk.vk_device, 1, &fence);
fence_pile_.push_back(fence);
return true;
});
item.second.erase(it, end);
}
}
GHOST_TSuccess GHOST_ContextVK::swapBufferRelease()
{
@@ -894,7 +937,18 @@ GHOST_TSuccess GHOST_ContextVK::swapBufferRelease()
VkResult present_result = VK_SUCCESS;
{
std::scoped_lock lock(device_vk.queue_mutex);
VkSwapchainPresentFenceInfoEXT fence_info{VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_FENCE_INFO_EXT};
VkFence present_fence = VK_NULL_HANDLE;
if (device_vk.use_vk_ext_swapchain_maintenance_1) {
present_fence = this->getFence();
fence_info.swapchainCount = 1;
fence_info.pFences = &present_fence;
present_info.pNext = &fence_info;
}
present_result = vkQueuePresentKHR(device_vk.generic_queue, &present_info);
this->setPresentFence(swapchain_, present_fence);
}
acquired_swapchain_image_index_.reset();
@@ -1312,11 +1366,25 @@ GHOST_TSuccess GHOST_ContextVK::recreateSwapchain(bool use_hdr_swapchain)
return GHOST_kSuccess;
}
void GHOST_ContextVK::destroySwapchainPresentFences(VkSwapchainKHR swapchain)
{
GHOST_DeviceVK &device_vk = vulkan_instance.value().device.value();
const std::vector<VkFence> &fences = present_fences_[swapchain];
if (!fences.empty()) {
vkWaitForFences(device_vk.vk_device, fences.size(), fences.data(), VK_TRUE, UINT64_MAX);
for (VkFence fence : fences) {
vkDestroyFence(device_vk.vk_device, fence, nullptr);
}
}
present_fences_.erase(swapchain);
}
GHOST_TSuccess GHOST_ContextVK::destroySwapchain()
{
GHOST_DeviceVK &device_vk = vulkan_instance.value().device.value();
if (swapchain_ != VK_NULL_HANDLE) {
this->destroySwapchainPresentFences(swapchain_);
vkDestroySwapchainKHR(device_vk.vk_device, swapchain_, nullptr);
}
device_vk.wait_idle();
@@ -1325,6 +1393,9 @@ GHOST_TSuccess GHOST_ContextVK::destroySwapchain()
}
swapchain_images_.clear();
for (GHOST_Frame &frame_data : frame_data_) {
for (VkSwapchainKHR swapchain : frame_data.discard_pile.swapchains) {
this->destroySwapchainPresentFences(swapchain);
}
frame_data.destroy(device_vk.vk_device);
}
frame_data_.clear();

View File

@@ -33,6 +33,7 @@
# endif
#endif
#include <map>
#include <optional>
#include <vector>
@@ -247,8 +248,15 @@ class GHOST_ContextVK : public GHOST_Context {
std::function<void(GHOST_VulkanOpenXRData *)> openxr_acquire_framebuffer_image_callback_;
std::function<void(GHOST_VulkanOpenXRData *)> openxr_release_framebuffer_image_callback_;
std::vector<VkFence> fence_pile_;
std::map<VkSwapchainKHR, std::vector<VkFence>> present_fences_;
const char *getPlatformSpecificSurfaceExtension() const;
GHOST_TSuccess recreateSwapchain(bool use_hdr_swapchain);
GHOST_TSuccess initializeFrameData();
GHOST_TSuccess destroySwapchain();
VkFence getFence();
void setPresentFence(VkSwapchainKHR swapchain, VkFence fence);
void destroySwapchainPresentFences(VkSwapchainKHR swapchain);
};