mirror of
https://github.com/EduApps-CDG/OpenDX
synced 2024-12-30 09:45:37 +01:00
[dxvk] Off-load command buffer submission to separate thread
Reduces load on the CS thread a bit, which may yield a small performance improvement.
This commit is contained in:
parent
4c0c66892a
commit
b35f3c14df
@ -262,8 +262,10 @@ namespace dxvk {
|
|||||||
VkResult DxvkDevice::presentImage(
|
VkResult DxvkDevice::presentImage(
|
||||||
const Rc<vk::Presenter>& presenter,
|
const Rc<vk::Presenter>& presenter,
|
||||||
VkSemaphore semaphore) {
|
VkSemaphore semaphore) {
|
||||||
std::lock_guard<std::mutex> queueLock(m_submissionLock);
|
DxvkPresentInfo presentInfo;
|
||||||
VkResult status = presenter->presentImage(semaphore);
|
presentInfo.presenter = presenter;
|
||||||
|
presentInfo.waitSync = semaphore;
|
||||||
|
VkResult status = m_submissionQueue.present(presentInfo);
|
||||||
|
|
||||||
if (status != VK_SUCCESS)
|
if (status != VK_SUCCESS)
|
||||||
return status;
|
return status;
|
||||||
@ -278,32 +280,22 @@ namespace dxvk {
|
|||||||
const Rc<DxvkCommandList>& commandList,
|
const Rc<DxvkCommandList>& commandList,
|
||||||
VkSemaphore waitSync,
|
VkSemaphore waitSync,
|
||||||
VkSemaphore wakeSync) {
|
VkSemaphore wakeSync) {
|
||||||
VkResult status;
|
DxvkSubmitInfo submitInfo;
|
||||||
|
submitInfo.cmdList = commandList;
|
||||||
{ // Queue submissions are not thread safe
|
submitInfo.queue = m_graphicsQueue.queueHandle;
|
||||||
std::lock_guard<std::mutex> queueLock(m_submissionLock);
|
submitInfo.waitSync = waitSync;
|
||||||
std::lock_guard<sync::Spinlock> statLock(m_statLock);
|
submitInfo.wakeSync = wakeSync;
|
||||||
|
m_submissionQueue.submit(submitInfo);
|
||||||
m_statCounters.merge(commandList->statCounters());
|
|
||||||
m_statCounters.addCtr(DxvkStatCounter::QueueSubmitCount, 1);
|
std::lock_guard<sync::Spinlock> statLock(m_statLock);
|
||||||
|
m_statCounters.merge(commandList->statCounters());
|
||||||
status = commandList->submit(
|
m_statCounters.addCtr(DxvkStatCounter::QueueSubmitCount, 1);
|
||||||
m_graphicsQueue.queueHandle,
|
|
||||||
waitSync, wakeSync);
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkDevice::waitForIdle() {
|
void DxvkDevice::waitForIdle() {
|
||||||
|
m_submissionQueue.synchronize();
|
||||||
|
|
||||||
if (m_vkd->vkDeviceWaitIdle(m_vkd->device()) != VK_SUCCESS)
|
if (m_vkd->vkDeviceWaitIdle(m_vkd->device()) != VK_SUCCESS)
|
||||||
Logger::err("DxvkDevice: waitForIdle: Operation failed");
|
Logger::err("DxvkDevice: waitForIdle: Operation failed");
|
||||||
}
|
}
|
||||||
|
@ -339,6 +339,7 @@ namespace dxvk {
|
|||||||
* presenter's \c presentImage method.
|
* presenter's \c presentImage method.
|
||||||
* \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
|
||||||
*/
|
*/
|
||||||
VkResult presentImage(
|
VkResult presentImage(
|
||||||
const Rc<vk::Presenter>& presenter,
|
const Rc<vk::Presenter>& presenter,
|
||||||
@ -347,11 +348,11 @@ namespace dxvk {
|
|||||||
/**
|
/**
|
||||||
* \brief Submits a command list
|
* \brief Submits a command list
|
||||||
*
|
*
|
||||||
* Synchronization arguments are optional.
|
* Submits the given command list to the device using
|
||||||
|
* the given set of optional synchronization primitives.
|
||||||
* \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,
|
||||||
@ -366,7 +367,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.lockDeviceQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -376,7 +378,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.unlockDeviceQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -430,7 +432,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;
|
||||||
|
|
||||||
|
@ -5,7 +5,8 @@ namespace dxvk {
|
|||||||
|
|
||||||
DxvkSubmissionQueue::DxvkSubmissionQueue(DxvkDevice* device)
|
DxvkSubmissionQueue::DxvkSubmissionQueue(DxvkDevice* device)
|
||||||
: m_device(device),
|
: m_device(device),
|
||||||
m_thread([this] () { threadFunc(); }) {
|
m_submitThread([this] () { submitCmdLists(); }),
|
||||||
|
m_finishThread([this] () { finishCmdLists(); }) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15,61 +16,132 @@ namespace dxvk {
|
|||||||
m_stopped.store(true);
|
m_stopped.store(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_condOnAdd.notify_one();
|
m_appendCond.notify_all();
|
||||||
m_thread.join();
|
m_submitCond.notify_all();
|
||||||
|
|
||||||
|
m_submitThread.join();
|
||||||
|
m_finishThread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkSubmissionQueue::submit(const Rc<DxvkCommandList>& cmdList) {
|
void DxvkSubmissionQueue::submit(DxvkSubmitInfo submitInfo) {
|
||||||
{ std::unique_lock<std::mutex> lock(m_mutex);
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
m_condOnTake.wait(lock, [this] {
|
m_finishCond.wait(lock, [this] {
|
||||||
return m_entries.size() < MaxNumQueuedCommandBuffers;
|
return m_submitQueue.size() + m_finishQueue.size() <= MaxNumQueuedCommandBuffers;
|
||||||
|
});
|
||||||
|
|
||||||
|
m_pending += 1;
|
||||||
|
m_submitQueue.push(std::move(submitInfo));
|
||||||
|
m_appendCond.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VkResult DxvkSubmissionQueue::present(DxvkPresentInfo presentInfo) {
|
||||||
|
this->synchronize();
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock(m_mutexQueue);
|
||||||
|
return presentInfo.presenter->presentImage(presentInfo.waitSync);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxvkSubmissionQueue::synchronize() {
|
||||||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
m_submitCond.wait(lock, [this] {
|
||||||
|
return m_submitQueue.empty();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxvkSubmissionQueue::lockDeviceQueue() {
|
||||||
|
m_mutexQueue.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxvkSubmissionQueue::unlockDeviceQueue() {
|
||||||
|
m_mutexQueue.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxvkSubmissionQueue::submitCmdLists() {
|
||||||
|
env::setThreadName("dxvk-submit");
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
while (!m_stopped.load()) {
|
||||||
|
m_appendCond.wait(lock, [this] {
|
||||||
|
return m_stopped.load() || !m_submitQueue.empty();
|
||||||
});
|
});
|
||||||
|
|
||||||
m_submits += 1;
|
if (m_stopped.load())
|
||||||
m_entries.push(cmdList);
|
return;
|
||||||
m_condOnAdd.notify_one();
|
|
||||||
|
DxvkSubmitInfo submitInfo = std::move(m_submitQueue.front());
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
|
// Submit command buffer to device
|
||||||
|
VkResult status;
|
||||||
|
|
||||||
|
{ std::lock_guard<std::mutex> lock(m_mutexQueue);
|
||||||
|
|
||||||
|
status = submitInfo.cmdList->submit(
|
||||||
|
submitInfo.queue,
|
||||||
|
submitInfo.waitSync,
|
||||||
|
submitInfo.wakeSync);
|
||||||
|
}
|
||||||
|
|
||||||
|
// On success, pass it on to the queue thread
|
||||||
|
lock = std::unique_lock<std::mutex>(m_mutex);
|
||||||
|
|
||||||
|
if (status == VK_SUCCESS) {
|
||||||
|
m_finishQueue.push(std::move(submitInfo));
|
||||||
|
m_submitQueue.pop();
|
||||||
|
m_submitCond.notify_all();
|
||||||
|
} else {
|
||||||
|
Logger::err(str::format(
|
||||||
|
"DxvkSubmissionQueue: Command submission failed with ",
|
||||||
|
status));
|
||||||
|
m_pending -= 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkSubmissionQueue::threadFunc() {
|
void DxvkSubmissionQueue::finishCmdLists() {
|
||||||
env::setThreadName("dxvk-queue");
|
env::setThreadName("dxvk-queue");
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
while (!m_stopped.load()) {
|
while (!m_stopped.load()) {
|
||||||
Rc<DxvkCommandList> cmdList;
|
m_submitCond.wait(lock, [this] {
|
||||||
|
return m_stopped.load() || !m_finishQueue.empty();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (m_stopped.load())
|
||||||
|
return;
|
||||||
|
|
||||||
{ std::unique_lock<std::mutex> lock(m_mutex);
|
DxvkSubmitInfo submitInfo = std::move(m_finishQueue.front());
|
||||||
|
lock.unlock();
|
||||||
m_condOnAdd.wait(lock, [this] {
|
|
||||||
return m_stopped.load() || (m_entries.size() != 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (m_entries.size() != 0) {
|
|
||||||
cmdList = std::move(m_entries.front());
|
|
||||||
m_entries.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_condOnTake.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmdList != nullptr) {
|
VkResult status = submitInfo.cmdList->synchronize();
|
||||||
VkResult status = cmdList->synchronize();
|
|
||||||
|
if (status == VK_SUCCESS) {
|
||||||
|
submitInfo.cmdList->signalEvents();
|
||||||
|
submitInfo.cmdList->reset();
|
||||||
|
|
||||||
if (status == VK_SUCCESS) {
|
m_device->recycleCommandList(submitInfo.cmdList);
|
||||||
cmdList->signalEvents();
|
} else {
|
||||||
cmdList->reset();
|
Logger::err(str::format(
|
||||||
|
"DxvkSubmissionQueue: Failed to sync fence: ",
|
||||||
m_device->recycleCommandList(cmdList);
|
status));
|
||||||
} else {
|
|
||||||
Logger::err(str::format(
|
|
||||||
"DxvkSubmissionQueue: Failed to sync fence: ",
|
|
||||||
status));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_submits -= 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lock = std::unique_lock<std::mutex>(m_mutex);
|
||||||
|
m_pending -= 1;
|
||||||
|
|
||||||
|
m_finishQueue.pop();
|
||||||
|
m_finishCond.notify_all();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,17 +6,45 @@
|
|||||||
|
|
||||||
#include "../util/thread.h"
|
#include "../util/thread.h"
|
||||||
|
|
||||||
|
#include "../vulkan/vulkan_presenter.h"
|
||||||
|
|
||||||
#include "dxvk_cmdlist.h"
|
#include "dxvk_cmdlist.h"
|
||||||
|
|
||||||
namespace dxvk {
|
namespace dxvk {
|
||||||
|
|
||||||
class DxvkDevice;
|
class DxvkDevice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Queue submission info
|
||||||
|
*
|
||||||
|
* Stores parameters used to submit
|
||||||
|
* a command buffer to the device.
|
||||||
|
*/
|
||||||
|
struct DxvkSubmitInfo {
|
||||||
|
Rc<DxvkCommandList> cmdList;
|
||||||
|
VkQueue queue;
|
||||||
|
VkSemaphore waitSync;
|
||||||
|
VkSemaphore wakeSync;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Present info
|
||||||
|
*
|
||||||
|
* Stores parameters used to present
|
||||||
|
* a swap chain image on the device.
|
||||||
|
*/
|
||||||
|
struct DxvkPresentInfo {
|
||||||
|
Rc<vk::Presenter> presenter;
|
||||||
|
VkSemaphore waitSync;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Submission queue
|
* \brief Submission queue
|
||||||
*/
|
*/
|
||||||
class DxvkSubmissionQueue {
|
class DxvkSubmissionQueue {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
DxvkSubmissionQueue(DxvkDevice* device);
|
DxvkSubmissionQueue(DxvkDevice* device);
|
||||||
@ -30,35 +58,84 @@ namespace dxvk {
|
|||||||
* \returns Pending submission count
|
* \returns Pending submission count
|
||||||
*/
|
*/
|
||||||
uint32_t pendingSubmissions() const {
|
uint32_t pendingSubmissions() const {
|
||||||
return m_submits.load();
|
return m_pending.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Submits a command list
|
* \brief Submits a command list asynchronously
|
||||||
*
|
*
|
||||||
* Submits a command list to the queue thread.
|
* Queues a command list for submission on the
|
||||||
* This thread will wait for the command list
|
* dedicated submission thread. Use this to take
|
||||||
* to finish executing on the GPU and signal
|
* the submission overhead off the calling thread.
|
||||||
* any queries and events that are used by
|
* \param [in] submitInfo Submission parameters
|
||||||
* the command list in question.
|
|
||||||
* \param [in] cmdList The command list
|
|
||||||
*/
|
*/
|
||||||
void submit(const Rc<DxvkCommandList>& cmdList);
|
void submit(
|
||||||
|
DxvkSubmitInfo submitInfo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Presents an image synchronously
|
||||||
|
*
|
||||||
|
* Waits for queued command lists to be submitted
|
||||||
|
* and then presents the current swap chain image
|
||||||
|
* of the presenter. May stall the calling thread.
|
||||||
|
* \param [in] present Present parameters
|
||||||
|
* \returns Status of the operation
|
||||||
|
*/
|
||||||
|
VkResult present(
|
||||||
|
DxvkPresentInfo present);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Synchronizes with queue submissions
|
||||||
|
*
|
||||||
|
* Waits for all pending command lists to be
|
||||||
|
* submitted to the GPU before returning.
|
||||||
|
*/
|
||||||
|
void synchronize();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Locks device queue
|
||||||
|
*
|
||||||
|
* Locks the mutex that protects the Vulkan queue
|
||||||
|
* that DXVK uses for command buffer submission.
|
||||||
|
* This is needed when the app submits its own
|
||||||
|
* command buffers to the queue.
|
||||||
|
*/
|
||||||
|
void lockDeviceQueue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Unlocks device queue
|
||||||
|
*
|
||||||
|
* Unlocks the mutex that protects the Vulkan
|
||||||
|
* queue used for command buffer submission.
|
||||||
|
*/
|
||||||
|
void unlockDeviceQueue();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
DxvkDevice* m_device;
|
DxvkDevice* m_device;
|
||||||
|
|
||||||
std::atomic<bool> m_stopped = { false };
|
std::atomic<bool> m_stopped = { false };
|
||||||
std::atomic<uint32_t> m_submits = { 0u };
|
std::atomic<uint32_t> m_pending = { 0u };
|
||||||
|
|
||||||
std::mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
std::condition_variable m_condOnAdd;
|
std::mutex m_mutexQueue;
|
||||||
std::condition_variable m_condOnTake;
|
|
||||||
std::queue<Rc<DxvkCommandList>> m_entries;
|
|
||||||
dxvk::thread m_thread;
|
|
||||||
|
|
||||||
void threadFunc();
|
std::condition_variable m_appendCond;
|
||||||
|
std::condition_variable m_submitCond;
|
||||||
|
std::condition_variable m_finishCond;
|
||||||
|
|
||||||
|
std::queue<DxvkSubmitInfo> m_submitQueue;
|
||||||
|
std::queue<DxvkSubmitInfo> m_finishQueue;
|
||||||
|
|
||||||
|
dxvk::thread m_submitThread;
|
||||||
|
dxvk::thread m_finishThread;
|
||||||
|
|
||||||
|
VkResult submitToQueue(
|
||||||
|
const DxvkSubmitInfo& submission);
|
||||||
|
|
||||||
|
void submitCmdLists();
|
||||||
|
|
||||||
|
void finishCmdLists();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user