mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-01-18 20:52:10 +01:00
[dxvk] Implement asynchronous presentation
Off-loads the vkQueuePresentKHR call to the queue submission thread to avoid synchronization with that thread on a present call.
This commit is contained in:
parent
0900f5d1bc
commit
ed5c43a14d
@ -306,8 +306,10 @@ namespace dxvk {
|
|||||||
m_context->endRecording(),
|
m_context->endRecording(),
|
||||||
sync.acquire, sync.present);
|
sync.acquire, sync.present);
|
||||||
|
|
||||||
status = m_device->presentImage(
|
m_device->presentImage(m_presenter,
|
||||||
m_presenter, sync.present);
|
sync.present, &m_presentStatus);
|
||||||
|
|
||||||
|
status = m_device->waitForSubmission(&m_presentStatus);
|
||||||
|
|
||||||
if (status != VK_SUCCESS)
|
if (status != VK_SUCCESS)
|
||||||
RecreateSwapChain(m_vsync);
|
RecreateSwapChain(m_vsync);
|
||||||
|
@ -113,6 +113,8 @@ namespace dxvk {
|
|||||||
|
|
||||||
D3D11Texture2D* m_backBuffer = nullptr;
|
D3D11Texture2D* m_backBuffer = nullptr;
|
||||||
|
|
||||||
|
DxvkSubmitStatus m_presentStatus;
|
||||||
|
|
||||||
std::vector<Rc<DxvkImageView>> m_imageViews;
|
std::vector<Rc<DxvkImageView>> m_imageViews;
|
||||||
|
|
||||||
bool m_dirty = true;
|
bool m_dirty = true;
|
||||||
|
@ -206,20 +206,19 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
VkResult DxvkDevice::presentImage(
|
void DxvkDevice::presentImage(
|
||||||
const Rc<vk::Presenter>& presenter,
|
const Rc<vk::Presenter>& presenter,
|
||||||
VkSemaphore semaphore) {
|
VkSemaphore semaphore,
|
||||||
|
DxvkSubmitStatus* status) {
|
||||||
|
status->result = VK_NOT_READY;
|
||||||
|
|
||||||
DxvkPresentInfo presentInfo;
|
DxvkPresentInfo presentInfo;
|
||||||
presentInfo.presenter = presenter;
|
presentInfo.presenter = presenter;
|
||||||
presentInfo.waitSync = semaphore;
|
presentInfo.waitSync = semaphore;
|
||||||
VkResult status = m_submissionQueue.present(presentInfo);
|
m_submissionQueue.present(presentInfo, status);
|
||||||
|
|
||||||
if (status != VK_SUCCESS)
|
|
||||||
return status;
|
|
||||||
|
|
||||||
std::lock_guard<sync::Spinlock> statLock(m_statLock);
|
std::lock_guard<sync::Spinlock> statLock(m_statLock);
|
||||||
m_statCounters.addCtr(DxvkStatCounter::QueuePresentCount, 1);
|
m_statCounters.addCtr(DxvkStatCounter::QueuePresentCount, 1);
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -239,6 +238,18 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VkResult DxvkDevice::waitForSubmission(DxvkSubmitStatus* status) {
|
||||||
|
VkResult result = status->result.load();
|
||||||
|
|
||||||
|
if (result == VK_NOT_READY) {
|
||||||
|
m_submissionQueue.synchronizeSubmission(status);
|
||||||
|
result = status->result.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkDevice::waitForIdle() {
|
void DxvkDevice::waitForIdle() {
|
||||||
m_submissionQueue.synchronize();
|
m_submissionQueue.synchronize();
|
||||||
|
|
||||||
|
@ -328,15 +328,17 @@ namespace dxvk {
|
|||||||
/**
|
/**
|
||||||
* \brief Presents a swap chain image
|
* \brief Presents a swap chain image
|
||||||
*
|
*
|
||||||
* Locks the device queues and invokes the
|
* Invokes the presenter's \c presentImage method on
|
||||||
* presenter's \c presentImage method.
|
* the submission thread. The status of this operation
|
||||||
|
* can be retrieved with \ref waitForSubmission.
|
||||||
* \param [in] presenter The presenter
|
* \param [in] presenter The presenter
|
||||||
* \param [in] semaphore Sync semaphore
|
* \param [in] semaphore Sync semaphore
|
||||||
* \returns Status of the operation
|
* \param [out] status Present status
|
||||||
*/
|
*/
|
||||||
VkResult presentImage(
|
void presentImage(
|
||||||
const Rc<vk::Presenter>& presenter,
|
const Rc<vk::Presenter>& presenter,
|
||||||
VkSemaphore semaphore);
|
VkSemaphore semaphore,
|
||||||
|
DxvkSubmitStatus* status);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Submits a command list
|
* \brief Submits a command list
|
||||||
@ -384,6 +386,14 @@ namespace dxvk {
|
|||||||
uint32_t pendingSubmissions() const {
|
uint32_t pendingSubmissions() const {
|
||||||
return m_submissionQueue.pendingSubmissions();
|
return m_submissionQueue.pendingSubmissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Waits for a given submission
|
||||||
|
*
|
||||||
|
* \param [in,out] status Submission status
|
||||||
|
* \returns Result of the submission
|
||||||
|
*/
|
||||||
|
VkResult waitForSubmission(DxvkSubmitStatus* status);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Waits until the device becomes idle
|
* \brief Waits until the device becomes idle
|
||||||
|
@ -31,17 +31,34 @@ namespace dxvk {
|
|||||||
return m_submitQueue.size() + m_finishQueue.size() <= MaxNumQueuedCommandBuffers;
|
return m_submitQueue.size() + m_finishQueue.size() <= MaxNumQueuedCommandBuffers;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
DxvkSubmitEntry entry = { };
|
||||||
|
entry.submit = std::move(submitInfo);
|
||||||
|
|
||||||
m_pending += 1;
|
m_pending += 1;
|
||||||
m_submitQueue.push(std::move(submitInfo));
|
m_submitQueue.push(std::move(entry));
|
||||||
m_appendCond.notify_all();
|
m_appendCond.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
VkResult DxvkSubmissionQueue::present(DxvkPresentInfo presentInfo) {
|
void DxvkSubmissionQueue::present(DxvkPresentInfo presentInfo, DxvkSubmitStatus* status) {
|
||||||
this->synchronize();
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lock(m_mutexQueue);
|
DxvkSubmitEntry entry = { };
|
||||||
return presentInfo.presenter->presentImage(presentInfo.waitSync);
|
entry.status = status;
|
||||||
|
entry.present = std::move(presentInfo);
|
||||||
|
|
||||||
|
m_submitQueue.push(std::move(entry));
|
||||||
|
m_appendCond.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxvkSubmissionQueue::synchronizeSubmission(
|
||||||
|
DxvkSubmitStatus* status) {
|
||||||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
m_submitCond.wait(lock, [status] {
|
||||||
|
return status->result.load() != VK_NOT_READY;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -77,32 +94,39 @@ namespace dxvk {
|
|||||||
if (m_stopped.load())
|
if (m_stopped.load())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DxvkSubmitInfo submitInfo = std::move(m_submitQueue.front());
|
DxvkSubmitEntry entry = std::move(m_submitQueue.front());
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
// Submit command buffer to device
|
// Submit command buffer to device
|
||||||
VkResult status;
|
VkResult status = VK_NOT_READY;
|
||||||
|
|
||||||
{ std::lock_guard<std::mutex> lock(m_mutexQueue);
|
{ std::lock_guard<std::mutex> lock(m_mutexQueue);
|
||||||
|
|
||||||
status = submitInfo.cmdList->submit(
|
if (entry.submit.cmdList != nullptr) {
|
||||||
submitInfo.waitSync,
|
status = entry.submit.cmdList->submit(
|
||||||
submitInfo.wakeSync);
|
entry.submit.waitSync,
|
||||||
|
entry.submit.wakeSync);
|
||||||
|
} else if (entry.present.presenter != nullptr) {
|
||||||
|
status = entry.present.presenter->presentImage(
|
||||||
|
entry.present.waitSync);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (entry.status)
|
||||||
|
entry.status->result = status;
|
||||||
|
|
||||||
// On success, pass it on to the queue thread
|
// On success, pass it on to the queue thread
|
||||||
lock = std::unique_lock<std::mutex>(m_mutex);
|
lock = std::unique_lock<std::mutex>(m_mutex);
|
||||||
|
|
||||||
if (status == VK_SUCCESS) {
|
if (status == VK_SUCCESS) {
|
||||||
m_finishQueue.push(std::move(submitInfo));
|
if (entry.submit.cmdList != nullptr)
|
||||||
m_submitQueue.pop();
|
m_finishQueue.push(std::move(entry));
|
||||||
m_submitCond.notify_all();
|
|
||||||
} else {
|
} else {
|
||||||
Logger::err(str::format(
|
Logger::err(str::format("DxvkSubmissionQueue: Command submission failed: ", status));
|
||||||
"DxvkSubmissionQueue: Command submission failed with ",
|
|
||||||
status));
|
|
||||||
m_pending -= 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_submitQueue.pop();
|
||||||
|
m_submitCond.notify_all();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,16 +144,16 @@ namespace dxvk {
|
|||||||
if (m_stopped.load())
|
if (m_stopped.load())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DxvkSubmitInfo submitInfo = std::move(m_finishQueue.front());
|
DxvkSubmitEntry entry = std::move(m_finishQueue.front());
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
VkResult status = submitInfo.cmdList->synchronize();
|
VkResult status = entry.submit.cmdList->synchronize();
|
||||||
|
|
||||||
if (status == VK_SUCCESS) {
|
if (status == VK_SUCCESS) {
|
||||||
submitInfo.cmdList->signalEvents();
|
entry.submit.cmdList->signalEvents();
|
||||||
submitInfo.cmdList->reset();
|
entry.submit.cmdList->reset();
|
||||||
|
|
||||||
m_device->recycleCommandList(submitInfo.cmdList);
|
m_device->recycleCommandList(entry.submit.cmdList);
|
||||||
} else {
|
} else {
|
||||||
Logger::err(str::format(
|
Logger::err(str::format(
|
||||||
"DxvkSubmissionQueue: Failed to sync fence: ",
|
"DxvkSubmissionQueue: Failed to sync fence: ",
|
||||||
|
@ -14,6 +14,17 @@ namespace dxvk {
|
|||||||
|
|
||||||
class DxvkDevice;
|
class DxvkDevice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Submission status
|
||||||
|
*
|
||||||
|
* Stores the result of a queue
|
||||||
|
* submission or a present call.
|
||||||
|
*/
|
||||||
|
struct DxvkSubmitStatus {
|
||||||
|
std::atomic<VkResult> result = { VK_SUCCESS };
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Queue submission info
|
* \brief Queue submission info
|
||||||
*
|
*
|
||||||
@ -39,6 +50,16 @@ namespace dxvk {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Submission queue entry
|
||||||
|
*/
|
||||||
|
struct DxvkSubmitEntry {
|
||||||
|
DxvkSubmitStatus* status;
|
||||||
|
DxvkSubmitInfo submit;
|
||||||
|
DxvkPresentInfo present;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Submission queue
|
* \brief Submission queue
|
||||||
*/
|
*/
|
||||||
@ -69,7 +90,7 @@ namespace dxvk {
|
|||||||
* \param [in] submitInfo Submission parameters
|
* \param [in] submitInfo Submission parameters
|
||||||
*/
|
*/
|
||||||
void submit(
|
void submit(
|
||||||
DxvkSubmitInfo submitInfo);
|
DxvkSubmitInfo submitInfo);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Presents an image synchronously
|
* \brief Presents an image synchronously
|
||||||
@ -80,8 +101,19 @@ namespace dxvk {
|
|||||||
* \param [in] present Present parameters
|
* \param [in] present Present parameters
|
||||||
* \returns Status of the operation
|
* \returns Status of the operation
|
||||||
*/
|
*/
|
||||||
VkResult present(
|
void present(
|
||||||
DxvkPresentInfo present);
|
DxvkPresentInfo presentInfo,
|
||||||
|
DxvkSubmitStatus* status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Synchronizes with one queue submission
|
||||||
|
*
|
||||||
|
* Waits for the result of the given submission
|
||||||
|
* or present operation to become available.
|
||||||
|
* \param [in,out] status Submission status
|
||||||
|
*/
|
||||||
|
void synchronizeSubmission(
|
||||||
|
DxvkSubmitStatus* status);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Synchronizes with queue submissions
|
* \brief Synchronizes with queue submissions
|
||||||
@ -123,8 +155,8 @@ namespace dxvk {
|
|||||||
std::condition_variable m_submitCond;
|
std::condition_variable m_submitCond;
|
||||||
std::condition_variable m_finishCond;
|
std::condition_variable m_finishCond;
|
||||||
|
|
||||||
std::queue<DxvkSubmitInfo> m_submitQueue;
|
std::queue<DxvkSubmitEntry> m_submitQueue;
|
||||||
std::queue<DxvkSubmitInfo> m_finishQueue;
|
std::queue<DxvkSubmitEntry> m_finishQueue;
|
||||||
|
|
||||||
dxvk::thread m_submitThread;
|
dxvk::thread m_submitThread;
|
||||||
dxvk::thread m_finishThread;
|
dxvk::thread m_finishThread;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user