1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-01-31 14:52:11 +01:00

[dxvk] Use timeline semaphores for GPU synchronization

Ditches the old fence and binary semaphore code.
This commit is contained in:
Philip Rebohle 2024-10-18 21:53:39 +02:00 committed by Philip Rebohle
parent 1ee60048c0
commit 27539fc838
4 changed files with 73 additions and 79 deletions

View File

@ -174,18 +174,6 @@ namespace dxvk {
const auto& graphicsQueue = m_device->queues().graphics;
const auto& transferQueue = m_device->queues().transfer;
VkSemaphoreCreateInfo semaphoreInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
if (m_vkd->vkCreateSemaphore(m_vkd->device(), &semaphoreInfo, nullptr, &m_bindSemaphore)
|| m_vkd->vkCreateSemaphore(m_vkd->device(), &semaphoreInfo, nullptr, &m_postSemaphore)
|| m_vkd->vkCreateSemaphore(m_vkd->device(), &semaphoreInfo, nullptr, &m_sdmaSemaphore))
throw DxvkError("DxvkCommandList: Failed to create semaphore");
VkFenceCreateInfo fenceInfo = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
if (m_vkd->vkCreateFence(m_vkd->device(), &fenceInfo, nullptr, &m_fence))
throw DxvkError("DxvkCommandList: Failed to create fence");
m_graphicsPool = new DxvkCommandPool(device, graphicsQueue.queueFamily);
if (transferQueue.queueFamily != graphicsQueue.queueFamily)
@ -197,16 +185,12 @@ namespace dxvk {
DxvkCommandList::~DxvkCommandList() {
this->reset();
m_vkd->vkDestroySemaphore(m_vkd->device(), m_bindSemaphore, nullptr);
m_vkd->vkDestroySemaphore(m_vkd->device(), m_postSemaphore, nullptr);
m_vkd->vkDestroySemaphore(m_vkd->device(), m_sdmaSemaphore, nullptr);
m_vkd->vkDestroyFence(m_vkd->device(), m_fence, nullptr);
}
VkResult DxvkCommandList::submit() {
VkResult DxvkCommandList::submit(
const DxvkTimelineSemaphores& semaphores,
DxvkTimelineSemaphoreValues& timelines) {
VkResult status = VK_SUCCESS;
const auto& graphics = m_device->queues().graphics;
@ -236,18 +220,16 @@ namespace dxvk {
if (sparseBind) {
// Sparse binding needs to serialize command execution, so wait
// for any prior submissions, then block any subsequent ones
m_commandSubmission.signalSemaphore(m_bindSemaphore, 0, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);
sparseBind->waitSemaphore(semaphores.graphics, timelines.graphics);
sparseBind->waitSemaphore(semaphores.transfer, timelines.transfer);
if ((status = m_commandSubmission.submit(m_device, graphics.queueHandle)))
return status;
sparseBind->waitSemaphore(m_bindSemaphore, 0);
sparseBind->signalSemaphore(m_postSemaphore, 0);
sparseBind->signalSemaphore(semaphores.graphics, ++timelines.graphics);
if ((status = sparseBind->submit(m_device, sparse.queueHandle)))
return status;
m_commandSubmission.waitSemaphore(m_postSemaphore, 0, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT);
m_commandSubmission.waitSemaphore(semaphores.graphics,
timelines.graphics, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT);
}
// Submit transfer commands as necessary
@ -257,12 +239,14 @@ namespace dxvk {
// If we had either a transfer command or a semaphore wait, submit to the
// transfer queue so that all subsequent commands get stalled as necessary.
if (m_device->hasDedicatedTransferQueue() && !m_commandSubmission.isEmpty()) {
m_commandSubmission.signalSemaphore(m_sdmaSemaphore, 0, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);
m_commandSubmission.signalSemaphore(semaphores.transfer,
++timelines.transfer, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);
if ((status = m_commandSubmission.submit(m_device, transfer.queueHandle)))
return status;
m_commandSubmission.waitSemaphore(m_sdmaSemaphore, 0, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT);
m_commandSubmission.waitSemaphore(semaphores.transfer,
timelines.transfer, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT);
}
// We promise to never do weird stuff to WSI images on
@ -291,14 +275,25 @@ namespace dxvk {
m_commandSubmission.signalSemaphore(m_wsiSemaphores.present,
0, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);
}
// Signal synchronization fence on final submission
m_commandSubmission.signalFence(m_fence);
}
m_commandSubmission.signalSemaphore(semaphores.graphics,
++timelines.graphics, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);
// Finally, submit all graphics commands of the current submission
if ((status = m_commandSubmission.submit(m_device, graphics.queueHandle)))
return status;
// If there are WSI semaphores involved, do another submit only
// containing a timeline semaphore signal so that we can be sure
// that they are safe to use afterwards.
if ((m_wsiSemaphores.present || m_wsiSemaphores.acquire) && isLast) {
m_commandSubmission.signalSemaphore(semaphores.graphics,
++timelines.graphics, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);
if ((status = m_commandSubmission.submit(m_device, graphics.queueHandle)))
return status;
}
}
return VK_SUCCESS;
@ -358,11 +353,6 @@ namespace dxvk {
}
VkResult DxvkCommandList::synchronizeFence() {
return m_vkd->vkWaitForFences(m_vkd->device(), 1, &m_fence, VK_TRUE, ~0ull);
}
void DxvkCommandList::reset() {
// Free resources and other objects
// that are no longer in use
@ -395,10 +385,6 @@ namespace dxvk {
// Reset actual command buffers and pools
m_graphicsPool->reset();
m_transferPool->reset();
// Reset fence
if (m_vkd->vkResetFences(m_vkd->device(), 1, &m_fence))
Logger::err("DxvkCommandList: Failed to reset fence");
}

View File

@ -18,6 +18,26 @@
namespace dxvk {
/**
* \brief Timeline semaphore pair
*
* One semaphore for each queue.
*/
struct DxvkTimelineSemaphores {
VkSemaphore graphics = VK_NULL_HANDLE;
VkSemaphore transfer = VK_NULL_HANDLE;
};
/**
* \brief Timeline semaphore values
*/
struct DxvkTimelineSemaphoreValues {
uint64_t graphics = 0u;
uint64_t transfer = 0u;
};
/**
* \brief Command buffer flags
*
@ -195,9 +215,14 @@ namespace dxvk {
/**
* \brief Submits command list
*
* \param [in] semaphores Timeline semaphore pair
* \param [in] timelines Timeline semaphore values
* \returns Submission status
*/
VkResult submit();
VkResult submit(
const DxvkTimelineSemaphores& semaphores,
DxvkTimelineSemaphoreValues& timelines);
/**
* \brief Stat counters
@ -341,12 +366,6 @@ namespace dxvk {
m_wsiSemaphores = wsiSemaphores;
}
/**
* \brief Synchronizes with command list fence
* \returns Return value of vkWaitForFences call
*/
VkResult synchronizeFence();
/**
* \brief Resets the command list
*
@ -1047,11 +1066,6 @@ namespace dxvk {
Rc<DxvkCommandPool> m_graphicsPool;
Rc<DxvkCommandPool> m_transferPool;
VkSemaphore m_bindSemaphore = VK_NULL_HANDLE;
VkSemaphore m_postSemaphore = VK_NULL_HANDLE;
VkSemaphore m_sdmaSemaphore = VK_NULL_HANDLE;
VkFence m_fence = VK_NULL_HANDLE;
DxvkCommandSubmissionInfo m_cmd;
PresenterSync m_wsiSemaphores = { };

View File

@ -141,10 +141,12 @@ namespace dxvk {
if (m_callback)
m_callback(true);
if (entry.submit.cmdList != nullptr)
entry.result = entry.submit.cmdList->submit();
else if (entry.present.presenter != nullptr)
if (entry.submit.cmdList != nullptr) {
entry.result = entry.submit.cmdList->submit(m_semaphores, m_timelines);
entry.timelines = m_timelines;
} else if (entry.present.presenter != nullptr) {
entry.result = entry.present.presenter->presentImage(entry.present.presentMode, entry.present.frameId);
}
if (m_callback)
m_callback(false);
@ -182,6 +184,8 @@ namespace dxvk {
void DxvkSubmissionQueue::finishCmdLists() {
env::setThreadName("dxvk-queue");
auto vk = m_device->vkd();
while (!m_stopped.load()) {
std::unique_lock<dxvk::mutex> lock(m_mutex);
@ -204,10 +208,19 @@ namespace dxvk {
if (entry.submit.cmdList != nullptr) {
VkResult status = m_lastError.load();
if (status != VK_ERROR_DEVICE_LOST)
status = entry.submit.cmdList->synchronizeFence();
if (status != VK_ERROR_DEVICE_LOST) {
std::array<VkSemaphore, 2> semaphores = { m_semaphores.graphics, m_semaphores.transfer };
std::array<uint64_t, 2> timelines = { entry.timelines.graphics, entry.timelines.transfer };
VkSemaphoreWaitInfo waitInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO };
waitInfo.semaphoreCount = semaphores.size();
waitInfo.pSemaphores = semaphores.data();
waitInfo.pValues = timelines.data();
status = vk->vkWaitSemaphores(vk->device(), &waitInfo, ~0ull);
}
if (status != VK_SUCCESS) {
m_lastError = status;

View File

@ -56,26 +56,7 @@ namespace dxvk {
DxvkSubmitStatus* status;
DxvkSubmitInfo submit;
DxvkPresentInfo present;
};
/**
* \brief Timeline semaphore pair
*
* One semaphore for each queue.
*/
struct DxvkTimelineSemaphores {
VkSemaphore graphics = VK_NULL_HANDLE;
VkSemaphore transfer = VK_NULL_HANDLE;
};
/**
* \brief Timeline semaphore values
*/
struct DxvkTimelineSemaphoreValues {
uint64_t graphics = 0u;
uint64_t transfer = 0u;
DxvkTimelineSemaphoreValues timelines;
};