mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-01-18 20:52:10 +01:00
[dxvk] Introduce DxvkDevice::waitForResource
Blocks on the queue thread's condition variable instead of busy-waiting, and tracks synchronization with new stat counters. Cleanup is rearranged to minimize delays before signals and resources are notified.
This commit is contained in:
parent
25ebf94873
commit
0c99b17081
@ -169,9 +169,8 @@ namespace dxvk {
|
|||||||
|
|
||||||
|
|
||||||
void DxvkCommandList::reset() {
|
void DxvkCommandList::reset() {
|
||||||
// Signal resources and events to
|
// Free resources and other objects
|
||||||
// avoid stalling main thread
|
// that are no longer in use
|
||||||
m_signalTracker.reset();
|
|
||||||
m_resources.reset();
|
m_resources.reset();
|
||||||
|
|
||||||
// Recycle heavy Vulkan objects
|
// Recycle heavy Vulkan objects
|
||||||
@ -185,6 +184,7 @@ namespace dxvk {
|
|||||||
m_gpuEventTracker.reset();
|
m_gpuEventTracker.reset();
|
||||||
|
|
||||||
// Less important stuff
|
// Less important stuff
|
||||||
|
m_signalTracker.reset();
|
||||||
m_statCounters.reset();
|
m_statCounters.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,9 +194,10 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Notifies signals
|
* \brief Notifies resources and signals
|
||||||
*/
|
*/
|
||||||
void notifySignals() {
|
void notifyObjects() {
|
||||||
|
m_resources.notify();
|
||||||
m_signalTracker.notify();
|
m_signalTracker.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,6 +246,24 @@ namespace dxvk {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxvkDevice::waitForResource(const Rc<DxvkResource>& resource, DxvkAccess access) {
|
||||||
|
if (resource->isInUse(access)) {
|
||||||
|
auto t0 = dxvk::high_resolution_clock::now();
|
||||||
|
|
||||||
|
m_submissionQueue.synchronizeUntil([resource, access] {
|
||||||
|
return !resource->isInUse(access);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto t1 = dxvk::high_resolution_clock::now();
|
||||||
|
auto us = std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0);
|
||||||
|
|
||||||
|
std::lock_guard<sync::Spinlock> lock(m_statLock);
|
||||||
|
m_statCounters.addCtr(DxvkStatCounter::GpuSyncCount, 1);
|
||||||
|
m_statCounters.addCtr(DxvkStatCounter::GpuSyncTicks, us.count());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkDevice::waitForIdle() {
|
void DxvkDevice::waitForIdle() {
|
||||||
|
@ -474,6 +474,14 @@ namespace dxvk {
|
|||||||
* \returns Result of the submission
|
* \returns Result of the submission
|
||||||
*/
|
*/
|
||||||
VkResult waitForSubmission(DxvkSubmitStatus* status);
|
VkResult waitForSubmission(DxvkSubmitStatus* status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Waits for resource to become idle
|
||||||
|
*
|
||||||
|
* \param [in] resource Resource to wait for
|
||||||
|
* \param [in] access Access mode to check
|
||||||
|
*/
|
||||||
|
void waitForResource(const Rc<DxvkResource>& resource, DxvkAccess access);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Waits until the device becomes idle
|
* \brief Waits until the device becomes idle
|
||||||
|
@ -6,9 +6,20 @@ namespace dxvk {
|
|||||||
DxvkLifetimeTracker::~DxvkLifetimeTracker() { }
|
DxvkLifetimeTracker::~DxvkLifetimeTracker() { }
|
||||||
|
|
||||||
|
|
||||||
void DxvkLifetimeTracker::reset() {
|
void DxvkLifetimeTracker::notify() {
|
||||||
for (const auto& resource : m_resources)
|
for (const auto& resource : m_resources)
|
||||||
resource.first->release(resource.second);
|
resource.first->release(resource.second);
|
||||||
|
|
||||||
|
m_notified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxvkLifetimeTracker::reset() {
|
||||||
|
// If this gets called without ever being submitted then
|
||||||
|
// we should at least report the resources as unused
|
||||||
|
if (!m_notified)
|
||||||
|
this->notify();
|
||||||
|
|
||||||
m_resources.clear();
|
m_resources.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,13 @@ namespace dxvk {
|
|||||||
rc->acquire(Access);
|
rc->acquire(Access);
|
||||||
m_resources.emplace_back(std::move(rc), Access);
|
m_resources.emplace_back(std::move(rc), Access);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Releases resources
|
||||||
|
*
|
||||||
|
* Marks all tracked resources as unused.
|
||||||
|
*/
|
||||||
|
void notify();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Resets the command list
|
* \brief Resets the command list
|
||||||
@ -42,6 +49,7 @@ namespace dxvk {
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
std::vector<std::pair<Rc<DxvkResource>, DxvkAccess>> m_resources;
|
std::vector<std::pair<Rc<DxvkResource>, DxvkAccess>> m_resources;
|
||||||
|
bool m_notified = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -140,9 +140,9 @@ namespace dxvk {
|
|||||||
void DxvkSubmissionQueue::finishCmdLists() {
|
void DxvkSubmissionQueue::finishCmdLists() {
|
||||||
env::setThreadName("dxvk-queue");
|
env::setThreadName("dxvk-queue");
|
||||||
|
|
||||||
std::unique_lock<dxvk::mutex> lock(m_mutex);
|
|
||||||
|
|
||||||
while (!m_stopped.load()) {
|
while (!m_stopped.load()) {
|
||||||
|
std::unique_lock<dxvk::mutex> lock(m_mutex);
|
||||||
|
|
||||||
if (m_finishQueue.empty()) {
|
if (m_finishQueue.empty()) {
|
||||||
auto t0 = dxvk::high_resolution_clock::now();
|
auto t0 = dxvk::high_resolution_clock::now();
|
||||||
|
|
||||||
@ -171,16 +171,21 @@ namespace dxvk {
|
|||||||
m_device->waitForIdle();
|
m_device->waitForIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.submit.cmdList->notifySignals();
|
// Release resources and signal events, then immediately wake
|
||||||
entry.submit.cmdList->reset();
|
// up any thread that's currently waiting on a resource in
|
||||||
|
// order to reduce delays as much as possible.
|
||||||
|
entry.submit.cmdList->notifyObjects();
|
||||||
|
|
||||||
m_device->recycleCommandList(entry.submit.cmdList);
|
lock.lock();
|
||||||
|
|
||||||
lock = std::unique_lock<dxvk::mutex>(m_mutex);
|
|
||||||
m_pending -= 1;
|
m_pending -= 1;
|
||||||
|
|
||||||
m_finishQueue.pop();
|
m_finishQueue.pop();
|
||||||
m_finishCond.notify_all();
|
m_finishCond.notify_all();
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
|
// Free the command list and associated objects now
|
||||||
|
entry.submit.cmdList->reset();
|
||||||
|
m_device->recycleCommandList(entry.submit.cmdList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,6 +145,18 @@ namespace dxvk {
|
|||||||
*/
|
*/
|
||||||
void synchronize();
|
void synchronize();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Synchronizes until a given condition becomes true
|
||||||
|
*
|
||||||
|
* Useful to wait for the GPU without busy-waiting.
|
||||||
|
* \param [in] pred Predicate to check
|
||||||
|
*/
|
||||||
|
template<typename Pred>
|
||||||
|
void synchronizeUntil(const Pred& pred) {
|
||||||
|
std::unique_lock<dxvk::mutex> lock(m_mutex);
|
||||||
|
m_finishCond.wait(lock, pred);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Locks device queue
|
* \brief Locks device queue
|
||||||
*
|
*
|
||||||
|
@ -20,6 +20,8 @@ namespace dxvk {
|
|||||||
PipeCompilerBusy, ///< Boolean indicating compiler activity
|
PipeCompilerBusy, ///< Boolean indicating compiler activity
|
||||||
QueueSubmitCount, ///< Number of command buffer submissions
|
QueueSubmitCount, ///< Number of command buffer submissions
|
||||||
QueuePresentCount, ///< Number of present calls / frames
|
QueuePresentCount, ///< Number of present calls / frames
|
||||||
|
GpuSyncCount, ///< Number of GPU synchronizations
|
||||||
|
GpuSyncTicks, ///< Time spent waiting for GPU
|
||||||
GpuIdleTicks, ///< GPU idle time in microseconds
|
GpuIdleTicks, ///< GPU idle time in microseconds
|
||||||
CsSyncCount, ///< CS thread synchronizations
|
CsSyncCount, ///< CS thread synchronizations
|
||||||
CsSyncTicks, ///< Time spent waiting on CS
|
CsSyncTicks, ///< Time spent waiting on CS
|
||||||
|
Loading…
x
Reference in New Issue
Block a user