1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-03-14 04:29:15 +01:00

[dxvk] Perform acquire and present synchronization in the backend

And add method to explicitly destroy relevant Vulkan objects.
This commit is contained in:
Philip Rebohle 2025-01-15 17:18:46 +01:00 committed by Philip Rebohle
parent b03ff775ce
commit 42adc4ac11
2 changed files with 59 additions and 14 deletions

View File

@ -70,14 +70,25 @@ namespace dxvk {
VkResult Presenter::acquireNextImage(PresenterSync& sync, Rc<DxvkImage>& image) {
std::lock_guard lock(m_surfaceMutex);
std::unique_lock lock(m_surfaceMutex);
// Don't acquire more than one image at a time
VkResult status = VK_SUCCESS;
m_surfaceCond.wait(lock, [this, &status] {
status = m_device->getDeviceStatus();
return !m_presentPending || status < 0;
});
if (status < 0)
return status;
// Ensure that the swap chain gets recreated if it is dirty
bool hasSwapchain = m_swapchain != VK_NULL_HANDLE;
updateSwapChain();
// Don't acquire more than one image at a time
// Don't acquire if we already did so after present
if (m_acquireStatus == VK_NOT_READY && m_swapchain) {
PresenterSync sync = m_semaphores.at(m_frameIndex);
@ -136,6 +147,7 @@ namespace dxvk {
sync = m_semaphores.at(m_frameIndex);
image = m_images.at(m_imageIndex);
m_presentPending = true;
return m_acquireStatus;
}
@ -185,25 +197,29 @@ namespace dxvk {
m_frameIndex %= m_semaphores.size();
}
// Recreate the swapchain on the next acquire, even if we get suboptimal.
// There is no guarantee that suboptimal state is returned by both functions.
if (status != VK_SUCCESS) {
Logger::info(str::format("Presenter: Got ", status, ", recreating swapchain"));
std::lock_guard lock(m_surfaceMutex);
m_dirtySwapchain = true;
return status;
}
// On a successful present, try to acquire next image already, in
// order to hide potential delays from the application thread.
if (status == VK_SUCCESS) {
PresenterSync& nextSync = m_semaphores.at(m_frameIndex);
waitForSwapchainFence(nextSync);
m_acquireStatus = m_vkd->vkAcquireNextImageKHR(m_vkd->device(),
m_swapchain, std::numeric_limits<uint64_t>::max(),
nextSync.acquire, VK_NULL_HANDLE, &m_imageIndex);
}
// Recreate the swapchain on the next acquire, even if we get suboptimal.
// There is no guarantee that suboptimal state is returned by both functions.
std::lock_guard lock(m_surfaceMutex);
if (status != VK_SUCCESS) {
Logger::info(str::format("Presenter: Got ", status, ", recreating swapchain"));
m_dirtySwapchain = true;
}
m_presentPending = false;
m_surfaceCond.notify_one();
return status;
}
@ -265,6 +281,19 @@ namespace dxvk {
}
void Presenter::destroyResources() {
std::unique_lock lock(m_surfaceMutex);
m_surfaceCond.wait(lock, [this] {
VkResult status = m_device->getDeviceStatus();
return !m_presentPending || status < 0;
});
destroySwapchain();
destroySurface();
}
void Presenter::setSyncInterval(uint32_t syncInterval) {
std::lock_guard lock(m_surfaceMutex);
@ -965,6 +994,8 @@ namespace dxvk {
m_swapchain = VK_NULL_HANDLE;
m_acquireStatus = VK_NOT_READY;
m_presentPending = false;
}

View File

@ -200,6 +200,17 @@ namespace dxvk {
*/
void invalidateSurface();
/**
* \brief Destroys resources immediately
*
* Blocks calling thread until pending swapchain operations
* have completed, and guarantees that the Vulkan swapchain
* and surface get destroyed before the function returns.
* This is useful to ensure that the application window can
* be reused, even if the presenter object is kept alive.
*/
void destroyResources();
private:
Rc<DxvkDevice> m_device;
@ -208,9 +219,11 @@ namespace dxvk {
Rc<vk::InstanceFn> m_vki;
Rc<vk::DeviceFn> m_vkd;
dxvk::mutex m_surfaceMutex;
PresenterSurfaceProc m_surfaceProc;
dxvk::mutex m_surfaceMutex;
dxvk::condition_variable m_surfaceCond;
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
VkSwapchainKHR m_swapchain = VK_NULL_HANDLE;
@ -234,6 +247,7 @@ namespace dxvk {
uint32_t m_frameIndex = 0;
VkResult m_acquireStatus = VK_NOT_READY;
bool m_presentPending = false;
std::optional<VkHdrMetadataEXT> m_hdrMetadata;
bool m_hdrMetadataDirty = false;