mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-02-20 19:54:19 +01:00
[dxvk] Use global timeline semaphore for command list synchronization
Replaces the old fence mechanism and also makes it easier to synchronize across queues.
This commit is contained in:
parent
cff9056915
commit
f385b4bb47
@ -11,14 +11,6 @@ namespace dxvk {
|
||||
const auto& graphicsQueue = m_device->queues().graphics;
|
||||
const auto& transferQueue = m_device->queues().transfer;
|
||||
|
||||
VkFenceCreateInfo fenceInfo;
|
||||
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||||
fenceInfo.pNext = nullptr;
|
||||
fenceInfo.flags = 0;
|
||||
|
||||
if (m_vkd->vkCreateFence(m_vkd->device(), &fenceInfo, nullptr, &m_fence) != VK_SUCCESS)
|
||||
throw DxvkError("DxvkCommandList: Failed to create fence");
|
||||
|
||||
VkCommandPoolCreateInfo poolInfo;
|
||||
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||
poolInfo.pNext = nullptr;
|
||||
@ -53,32 +45,20 @@ namespace dxvk {
|
||||
|| m_vkd->vkAllocateCommandBuffers(m_vkd->device(), &cmdInfoGfx, &m_initBuffer) != VK_SUCCESS
|
||||
|| m_vkd->vkAllocateCommandBuffers(m_vkd->device(), &cmdInfoDma, &m_sdmaBuffer) != VK_SUCCESS)
|
||||
throw DxvkError("DxvkCommandList: Failed to allocate command buffer");
|
||||
|
||||
if (m_device->hasDedicatedTransferQueue()) {
|
||||
VkSemaphoreCreateInfo semInfo;
|
||||
semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||
semInfo.pNext = nullptr;
|
||||
semInfo.flags = 0;
|
||||
|
||||
if (m_vkd->vkCreateSemaphore(m_vkd->device(), &semInfo, nullptr, &m_sdmaSemaphore) != VK_SUCCESS)
|
||||
throw DxvkError("DxvkCommandList: Failed to create semaphore");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DxvkCommandList::~DxvkCommandList() {
|
||||
this->reset();
|
||||
|
||||
m_vkd->vkDestroySemaphore(m_vkd->device(), m_sdmaSemaphore, nullptr);
|
||||
|
||||
m_vkd->vkDestroyCommandPool(m_vkd->device(), m_graphicsPool, nullptr);
|
||||
m_vkd->vkDestroyCommandPool(m_vkd->device(), m_transferPool, nullptr);
|
||||
|
||||
m_vkd->vkDestroyFence(m_vkd->device(), m_fence, nullptr);
|
||||
}
|
||||
|
||||
|
||||
VkResult DxvkCommandList::submit() {
|
||||
VkResult DxvkCommandList::submit(
|
||||
VkSemaphore semaphore,
|
||||
uint64_t& semaphoreValue) {
|
||||
const auto& graphics = m_device->queues().graphics;
|
||||
const auto& transfer = m_device->queues().transfer;
|
||||
|
||||
@ -91,11 +71,12 @@ namespace dxvk {
|
||||
|
||||
if (m_device->hasDedicatedTransferQueue()) {
|
||||
VkSemaphoreSubmitInfo signalInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO };
|
||||
signalInfo.semaphore = m_sdmaSemaphore;
|
||||
signalInfo.semaphore = semaphore;
|
||||
signalInfo.value = ++semaphoreValue;
|
||||
signalInfo.stageMask = VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT;
|
||||
m_submission.signalInfos.push_back(signalInfo);
|
||||
|
||||
VkResult status = submitToQueue(transfer.queueHandle, VK_NULL_HANDLE, m_submission);
|
||||
VkResult status = submitToQueue(transfer.queueHandle, m_submission);
|
||||
|
||||
if (status != VK_SUCCESS)
|
||||
return status;
|
||||
@ -103,7 +84,8 @@ namespace dxvk {
|
||||
m_submission.reset();
|
||||
|
||||
VkSemaphoreSubmitInfo waitInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO };
|
||||
waitInfo.semaphore = m_sdmaSemaphore;
|
||||
waitInfo.semaphore = semaphore;
|
||||
waitInfo.value = semaphoreValue;
|
||||
waitInfo.stageMask = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT;
|
||||
m_submission.waitInfos.push_back(waitInfo);
|
||||
}
|
||||
@ -151,20 +133,14 @@ namespace dxvk {
|
||||
m_submission.signalInfos.push_back(signalInfo);
|
||||
}
|
||||
|
||||
return submitToQueue(graphics.queueHandle, m_fence, m_submission);
|
||||
}
|
||||
|
||||
|
||||
VkResult DxvkCommandList::synchronize() {
|
||||
VkResult status = VK_TIMEOUT;
|
||||
|
||||
while (status == VK_TIMEOUT) {
|
||||
status = m_vkd->vkWaitForFences(
|
||||
m_vkd->device(), 1, &m_fence, VK_FALSE,
|
||||
1'000'000'000ull);
|
||||
}
|
||||
|
||||
return status;
|
||||
// Signal global timeline semaphore
|
||||
VkSemaphoreSubmitInfo signalInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO };
|
||||
signalInfo.semaphore = semaphore;
|
||||
signalInfo.value = ++semaphoreValue;
|
||||
signalInfo.stageMask = VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT;
|
||||
m_submission.signalInfos.push_back(signalInfo);
|
||||
|
||||
return submitToQueue(graphics.queueHandle, m_submission);
|
||||
}
|
||||
|
||||
|
||||
@ -184,9 +160,6 @@ namespace dxvk {
|
||||
|| m_vkd->vkBeginCommandBuffer(m_sdmaBuffer, &info) != VK_SUCCESS)
|
||||
Logger::err("DxvkCommandList: Failed to begin command buffer");
|
||||
|
||||
if (m_vkd->vkResetFences(m_vkd->device(), 1, &m_fence) != VK_SUCCESS)
|
||||
Logger::err("DxvkCommandList: Failed to reset fence");
|
||||
|
||||
// Unconditionally mark the exec buffer as used. There
|
||||
// is virtually no use case where this isn't correct.
|
||||
m_cmdBuffersUsed = DxvkCmdBuffer::ExecBuffer;
|
||||
@ -238,7 +211,6 @@ namespace dxvk {
|
||||
|
||||
VkResult DxvkCommandList::submitToQueue(
|
||||
VkQueue queue,
|
||||
VkFence fence,
|
||||
const DxvkQueueSubmission& info) {
|
||||
VkSubmitInfo2 submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO_2 };
|
||||
submitInfo.waitSemaphoreInfoCount = info.waitInfos.size();
|
||||
@ -248,7 +220,7 @@ namespace dxvk {
|
||||
submitInfo.signalSemaphoreInfoCount = info.signalInfos.size();
|
||||
submitInfo.pSignalSemaphoreInfos = info.signalInfos.data();
|
||||
|
||||
return m_vkd->vkQueueSubmit2(queue, 1, &submitInfo, fence);
|
||||
return m_vkd->vkQueueSubmit2(queue, 1, &submitInfo, VK_NULL_HANDLE);
|
||||
}
|
||||
|
||||
void DxvkCommandList::cmdBeginDebugUtilsLabel(VkDebugUtilsLabelEXT *pLabelInfo) {
|
||||
|
@ -72,19 +72,17 @@ namespace dxvk {
|
||||
/**
|
||||
* \brief Submits command list
|
||||
*
|
||||
* \param [in] queue Device queue
|
||||
* \param [in] semaphore Global timeline semaphore
|
||||
* \param [in,out] semaphoreValue Semaphore value. On input,
|
||||
* this is the last signaled value of the semaphore so that
|
||||
* synchronization can take place as needed. On ouput, this
|
||||
* will contain the new value the semaphore gets signaled
|
||||
* to by this submission.
|
||||
* \returns Submission status
|
||||
*/
|
||||
VkResult submit();
|
||||
|
||||
/**
|
||||
* \brief Synchronizes command buffer execution
|
||||
*
|
||||
* Waits for the fence associated with
|
||||
* this command buffer to get signaled.
|
||||
* \returns Synchronization status
|
||||
*/
|
||||
VkResult synchronize();
|
||||
VkResult submit(
|
||||
VkSemaphore semaphore,
|
||||
uint64_t& semaphoreValue);
|
||||
|
||||
/**
|
||||
* \brief Stat counters
|
||||
@ -813,8 +811,6 @@ namespace dxvk {
|
||||
Rc<vk::DeviceFn> m_vkd;
|
||||
Rc<vk::InstanceFn> m_vki;
|
||||
|
||||
VkFence m_fence;
|
||||
|
||||
VkCommandPool m_graphicsPool = VK_NULL_HANDLE;
|
||||
VkCommandPool m_transferPool = VK_NULL_HANDLE;
|
||||
|
||||
@ -822,8 +818,6 @@ namespace dxvk {
|
||||
VkCommandBuffer m_initBuffer = VK_NULL_HANDLE;
|
||||
VkCommandBuffer m_sdmaBuffer = VK_NULL_HANDLE;
|
||||
|
||||
VkSemaphore m_sdmaSemaphore = VK_NULL_HANDLE;
|
||||
|
||||
vk::PresenterSync m_wsiSemaphores = { };
|
||||
|
||||
DxvkCmdBufferFlags m_cmdBuffersUsed;
|
||||
@ -853,7 +847,6 @@ namespace dxvk {
|
||||
|
||||
VkResult submitToQueue(
|
||||
VkQueue queue,
|
||||
VkFence fence,
|
||||
const DxvkQueueSubmission& info);
|
||||
|
||||
};
|
||||
|
@ -7,20 +7,34 @@ namespace dxvk {
|
||||
: m_device(device),
|
||||
m_submitThread([this] () { submitCmdLists(); }),
|
||||
m_finishThread([this] () { finishCmdLists(); }) {
|
||||
auto vk = m_device->vkd();
|
||||
|
||||
VkSemaphoreTypeCreateInfo typeInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO };
|
||||
typeInfo.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE;
|
||||
|
||||
VkSemaphoreCreateInfo info = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, &typeInfo };
|
||||
|
||||
if (vk->vkCreateSemaphore(vk->device(), &info, nullptr, &m_semaphore))
|
||||
throw DxvkError("Failed to create global timeline semaphore");
|
||||
}
|
||||
|
||||
|
||||
DxvkSubmissionQueue::~DxvkSubmissionQueue() {
|
||||
auto vk = m_device->vkd();
|
||||
|
||||
{ std::unique_lock<dxvk::mutex> lock(m_mutex);
|
||||
m_stopped.store(true);
|
||||
}
|
||||
|
||||
|
||||
m_appendCond.notify_all();
|
||||
m_submitCond.notify_all();
|
||||
|
||||
m_submitThread.join();
|
||||
m_finishThread.join();
|
||||
|
||||
synchronizeSemaphore(m_semaphoreValue);
|
||||
|
||||
vk->vkDestroySemaphore(vk->device(), m_semaphore, nullptr);
|
||||
}
|
||||
|
||||
|
||||
@ -81,6 +95,24 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
VkResult DxvkSubmissionQueue::synchronizeSemaphore(
|
||||
uint64_t semaphoreValue) {
|
||||
auto vk = m_device->vkd();
|
||||
|
||||
VkSemaphoreWaitInfo waitInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO };
|
||||
waitInfo.semaphoreCount = 1;
|
||||
waitInfo.pSemaphores = &m_semaphore;
|
||||
waitInfo.pValues = &semaphoreValue;
|
||||
|
||||
VkResult vr = vk->vkWaitSemaphores(vk->device(), &waitInfo, ~0ull);
|
||||
|
||||
if (vr)
|
||||
Logger::err(str::format("Failed to synchronize with global timeline semaphore: ", vr));
|
||||
|
||||
return vr;
|
||||
}
|
||||
|
||||
|
||||
void DxvkSubmissionQueue::submitCmdLists() {
|
||||
env::setThreadName("dxvk-submit");
|
||||
|
||||
@ -104,7 +136,8 @@ namespace dxvk {
|
||||
std::lock_guard<dxvk::mutex> lock(m_mutexQueue);
|
||||
|
||||
if (entry.submit.cmdList != nullptr) {
|
||||
status = entry.submit.cmdList->submit();
|
||||
status = entry.submit.cmdList->submit(m_semaphore, m_semaphoreValue);
|
||||
entry.submit.semaphoreValue = m_semaphoreValue;
|
||||
} else if (entry.present.presenter != nullptr) {
|
||||
status = entry.present.presenter->presentImage();
|
||||
}
|
||||
@ -161,7 +194,7 @@ namespace dxvk {
|
||||
VkResult status = m_lastError.load();
|
||||
|
||||
if (status != VK_ERROR_DEVICE_LOST)
|
||||
status = entry.submit.cmdList->synchronize();
|
||||
status = synchronizeSemaphore(entry.submit.semaphoreValue);
|
||||
|
||||
if (status != VK_SUCCESS) {
|
||||
Logger::err(str::format("DxvkSubmissionQueue: Failed to sync fence: ", status));
|
||||
|
@ -33,6 +33,7 @@ namespace dxvk {
|
||||
*/
|
||||
struct DxvkSubmitInfo {
|
||||
Rc<DxvkCommandList> cmdList;
|
||||
uint64_t semaphoreValue;
|
||||
};
|
||||
|
||||
|
||||
@ -175,13 +176,16 @@ namespace dxvk {
|
||||
|
||||
private:
|
||||
|
||||
DxvkDevice* m_device;
|
||||
DxvkDevice* m_device;
|
||||
|
||||
std::atomic<VkResult> m_lastError = { VK_SUCCESS };
|
||||
std::atomic<VkResult> m_lastError = { VK_SUCCESS };
|
||||
|
||||
std::atomic<bool> m_stopped = { false };
|
||||
std::atomic<uint32_t> m_pending = { 0u };
|
||||
std::atomic<uint64_t> m_gpuIdle = { 0ull };
|
||||
std::atomic<bool> m_stopped = { false };
|
||||
std::atomic<uint32_t> m_pending = { 0u };
|
||||
std::atomic<uint64_t> m_gpuIdle = { 0ull };
|
||||
|
||||
VkSemaphore m_semaphore = VK_NULL_HANDLE;
|
||||
uint64_t m_semaphoreValue = 0ull;
|
||||
|
||||
dxvk::mutex m_mutex;
|
||||
dxvk::mutex m_mutexQueue;
|
||||
@ -196,8 +200,8 @@ namespace dxvk {
|
||||
dxvk::thread m_submitThread;
|
||||
dxvk::thread m_finishThread;
|
||||
|
||||
VkResult submitToQueue(
|
||||
const DxvkSubmitInfo& submission);
|
||||
VkResult synchronizeSemaphore(
|
||||
uint64_t semaphoreValue);
|
||||
|
||||
void submitCmdLists();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user