1
0
mirror of https://github.com/EduApps-CDG/OpenDX synced 2024-12-30 09:45:37 +01:00

[dxvk] Move command submission to separate thread

- Reduces load on the CS thread and the main rendering thread.
- This takes into account that VR apps access the queue as well.
This commit is contained in:
Philip Rebohle 2018-09-20 08:01:30 +02:00
parent 539cc77b6e
commit f61ff5d345
No known key found for this signature in database
GPG Key ID: C8CC613427A31C99
4 changed files with 166 additions and 74 deletions

View File

@ -221,13 +221,16 @@ namespace dxvk {
VkResult DxvkDevice::presentSwapImage( VkResult DxvkDevice::presentSwapImage(
const VkPresentInfoKHR& presentInfo) { const VkPresentInfoKHR& presentInfo) {
{ // Queue submissions are not thread safe { std::lock_guard<sync::Spinlock> statLock(m_statLock);
std::lock_guard<std::mutex> queueLock(m_submissionLock);
std::lock_guard<sync::Spinlock> statLock(m_statLock);
m_statCounters.addCtr(DxvkStatCounter::QueuePresentCount, 1); m_statCounters.addCtr(DxvkStatCounter::QueuePresentCount, 1);
return m_vkd->vkQueuePresentKHR(m_presentQueue.queueHandle, &presentInfo);
} }
this->lockSubmission();
VkResult status = m_vkd->vkQueuePresentKHR(
m_presentQueue.queueHandle, &presentInfo);
this->unlockSubmission();
return status;
} }
@ -235,41 +238,17 @@ namespace dxvk {
const Rc<DxvkCommandList>& commandList, const Rc<DxvkCommandList>& commandList,
const Rc<DxvkSemaphore>& waitSync, const Rc<DxvkSemaphore>& waitSync,
const Rc<DxvkSemaphore>& wakeSync) { const Rc<DxvkSemaphore>& wakeSync) {
VkSemaphore waitSemaphore = VK_NULL_HANDLE; { std::lock_guard<sync::Spinlock> statLock(m_statLock);
VkSemaphore wakeSemaphore = VK_NULL_HANDLE;
if (waitSync != nullptr) {
waitSemaphore = waitSync->handle();
commandList->trackResource(waitSync);
}
if (wakeSync != nullptr) {
wakeSemaphore = wakeSync->handle();
commandList->trackResource(wakeSync);
}
VkResult status;
{ // Queue submissions are not thread safe
std::lock_guard<std::mutex> queueLock(m_submissionLock);
std::lock_guard<sync::Spinlock> statLock(m_statLock);
m_statCounters.merge(commandList->statCounters()); m_statCounters.merge(commandList->statCounters());
m_statCounters.addCtr(DxvkStatCounter::QueueSubmitCount, 1); m_statCounters.addCtr(DxvkStatCounter::QueueSubmitCount, 1);
status = commandList->submit(
m_graphicsQueue.queueHandle,
waitSemaphore, wakeSemaphore);
}
if (status == VK_SUCCESS) {
// Add this to the set of running submissions
m_submissionQueue.submit(commandList);
} else {
Logger::err(str::format(
"DxvkDevice: Command buffer submission failed: ",
status));
} }
DxvkSubmission submission;
submission.cmdList = commandList;
submission.semWait = waitSync;
submission.semWake = wakeSync;
m_submissionQueue.submit(std::move(submission));
} }

View File

@ -327,7 +327,6 @@ namespace dxvk {
* \param [in] commandList The command list to submit * \param [in] commandList The command list to submit
* \param [in] waitSync (Optional) Semaphore to wait on * \param [in] waitSync (Optional) Semaphore to wait on
* \param [in] wakeSync (Optional) Semaphore to notify * \param [in] wakeSync (Optional) Semaphore to notify
* \returns Synchronization fence
*/ */
void submitCommandList( void submitCommandList(
const Rc<DxvkCommandList>& commandList, const Rc<DxvkCommandList>& commandList,
@ -342,7 +341,8 @@ namespace dxvk {
* to lock the queue before submitting command buffers. * to lock the queue before submitting command buffers.
*/ */
void lockSubmission() { void lockSubmission() {
m_submissionLock.lock(); m_submissionQueue.synchronize();
m_submissionQueue.lock();
} }
/** /**
@ -352,7 +352,7 @@ namespace dxvk {
* itself can use them for submissions again. * itself can use them for submissions again.
*/ */
void unlockSubmission() { void unlockSubmission() {
m_submissionLock.unlock(); m_submissionQueue.unlock();
} }
/** /**
@ -399,7 +399,6 @@ namespace dxvk {
sync::Spinlock m_statLock; sync::Spinlock m_statLock;
DxvkStatCounters m_statCounters; DxvkStatCounters m_statCounters;
std::mutex m_submissionLock;
DxvkDeviceQueue m_graphicsQueue; DxvkDeviceQueue m_graphicsQueue;
DxvkDeviceQueue m_presentQueue; DxvkDeviceQueue m_presentQueue;

View File

@ -4,54 +4,130 @@
namespace dxvk { namespace dxvk {
DxvkSubmissionQueue::DxvkSubmissionQueue(DxvkDevice* device) DxvkSubmissionQueue::DxvkSubmissionQueue(DxvkDevice* device)
: m_device(device), : m_device (device),
m_thread([this] () { threadFunc(); }) { m_queueThread ([this] () { threadQueue(); }),
m_submitThread([this] () { threadSubmit(); }) {
} }
DxvkSubmissionQueue::~DxvkSubmissionQueue() { DxvkSubmissionQueue::~DxvkSubmissionQueue() {
{ std::unique_lock<std::mutex> lock(m_mutex); { std::unique_lock<std::mutex> lockQueue (m_queueLock);
std::unique_lock<std::mutex> lockSubmit(m_submitLock);
m_stopped.store(true); m_stopped.store(true);
} }
m_condOnAdd.notify_one(); m_submitCondOnAdd.notify_one();
m_thread.join(); m_submitThread.join();
m_queueCond.notify_one();
m_queueThread.join();
} }
void DxvkSubmissionQueue::submit(const Rc<DxvkCommandList>& cmdList) { void DxvkSubmissionQueue::submit(DxvkSubmission submission) {
{ std::unique_lock<std::mutex> lock(m_mutex); std::unique_lock<std::mutex> lock(m_submitLock);
m_condOnTake.wait(lock, [this] { m_submitCondOnTake.wait(lock, [this] {
return m_entries.size() < MaxNumQueuedCommandBuffers; return m_submitQueue.size() < MaxNumQueuedCommandBuffers;
}); });
m_submits += 1; m_submits += 1;
m_entries.push(cmdList); m_submitQueue.push(std::move(submission));
m_condOnAdd.notify_one(); m_submitCondOnAdd.notify_one();
}
void DxvkSubmissionQueue::synchronize() {
std::unique_lock<std::mutex> lock(m_submitLock);
m_submitCondOnTake.wait(lock, [this] {
return m_submitQueue.size() == 0;
});
}
void DxvkSubmissionQueue::threadSubmit() {
env::setThreadName(L"dxvk-submit");
while (!m_stopped.load()) {
DxvkSubmission submission;
{ std::unique_lock<std::mutex> lock(m_submitLock);
m_submitCondOnAdd.wait(lock, [this] {
return m_stopped.load() || (m_submitQueue.size() != 0);
});
if (m_submitQueue.size() != 0)
submission = std::move(m_submitQueue.front());
}
if (submission.cmdList != nullptr) {
// Make sure that the semaphores do not get deleted or
// reused before the command submission has completed
VkSemaphore waitSemaphore = VK_NULL_HANDLE;
VkSemaphore wakeSemaphore = VK_NULL_HANDLE;
if (submission.semWait != nullptr) {
waitSemaphore = submission.semWait->handle();
submission.cmdList->trackResource(submission.semWait);
}
if (submission.semWake != nullptr) {
wakeSemaphore = submission.semWake->handle();
submission.cmdList->trackResource(submission.semWake);
}
// Protect the Vulkan queue itself from concurrent access
{ std::unique_lock<std::mutex> lock(m_externalLock);
VkResult status = submission.cmdList->submit(
m_device->m_graphicsQueue.queueHandle,
waitSemaphore, wakeSemaphore);
if (status != VK_SUCCESS)
Logger::err(str::format("Dxvk: Submission failed: ", status));
}
// Process this submission on the 'queue' thread
{ std::unique_lock<std::mutex> lock(m_queueLock);
m_queueEntries.push(std::move(submission.cmdList));
m_queueCond.notify_one();
}
// Remove submission now. We cannot do this earlier as
// the synchronize method depends on this behaviour.
{ std::unique_lock<std::mutex> lock(m_submitLock);
if (m_submitQueue.size() != 0)
m_submitQueue.pop();
}
}
m_submitCondOnTake.notify_one();
} }
} }
void DxvkSubmissionQueue::threadFunc() { void DxvkSubmissionQueue::threadQueue() {
env::setThreadName(L"dxvk-queue"); env::setThreadName(L"dxvk-queue");
while (!m_stopped.load()) { while (!m_stopped.load()) {
Rc<DxvkCommandList> cmdList; Rc<DxvkCommandList> cmdList;
{ std::unique_lock<std::mutex> lock(m_mutex); { std::unique_lock<std::mutex> lock(m_queueLock);
m_condOnAdd.wait(lock, [this] { m_queueCond.wait(lock, [this] {
return m_stopped.load() || (m_entries.size() != 0); return m_stopped.load() || (m_queueEntries.size() != 0);
}); });
if (m_entries.size() != 0) { if (m_queueEntries.size() != 0) {
cmdList = std::move(m_entries.front()); cmdList = std::move(m_queueEntries.front());
m_entries.pop(); m_queueEntries.pop();
} }
m_condOnTake.notify_one();
} }
if (cmdList != nullptr) { if (cmdList != nullptr) {

View File

@ -11,6 +11,12 @@
namespace dxvk { namespace dxvk {
class DxvkDevice; class DxvkDevice;
struct DxvkSubmission {
Rc<DxvkCommandList> cmdList;
Rc<DxvkSemaphore> semWait;
Rc<DxvkSemaphore> semWake;
};
/** /**
* \brief Submission queue * \brief Submission queue
@ -41,9 +47,33 @@ namespace dxvk {
* to finish executing on the GPU and signal * to finish executing on the GPU and signal
* any queries and events that are used by * any queries and events that are used by
* the command list in question. * the command list in question.
* \param [in] cmdList The command list * \param [in] submission Command submission
*/ */
void submit(const Rc<DxvkCommandList>& cmdList); void submit(DxvkSubmission submission);
/**
* \brief Synchronizes with submission thread
*
* Waits until all submissions queued prior
* to this call are submitted to the GPU.
*/
void synchronize();
/**
* \brief Locks external queue lock
* Protects the Vulkan queue.
*/
void lock() {
m_externalLock.lock();
}
/**
* \brief Locks external queue lock
* Releases the Vulkan queue.
*/
void unlock() {
m_externalLock.unlock();
}
private: private:
@ -51,14 +81,22 @@ namespace dxvk {
std::atomic<bool> m_stopped = { false }; std::atomic<bool> m_stopped = { false };
std::atomic<uint32_t> m_submits = { 0u }; std::atomic<uint32_t> m_submits = { 0u };
std::mutex m_externalLock;
std::mutex m_mutex; std::mutex m_queueLock;
std::condition_variable m_condOnAdd; std::condition_variable m_queueCond;
std::condition_variable m_condOnTake; std::queue<Rc<DxvkCommandList>> m_queueEntries;
std::queue<Rc<DxvkCommandList>> m_entries; dxvk::thread m_queueThread;
dxvk::thread m_thread;
void threadFunc(); std::mutex m_submitLock;
std::condition_variable m_submitCondOnAdd;
std::condition_variable m_submitCondOnTake;
std::queue<DxvkSubmission> m_submitQueue;
dxvk::thread m_submitThread;
void threadSubmit();
void threadQueue();
}; };