1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-01-18 20:52:10 +01:00

[util] Reimplement Signal

The new implementation is more akin to D3D12 fences or timeline
semaphores, and should make implementing similar concepts easier.
This commit is contained in:
Philip Rebohle 2019-11-25 17:37:16 +01:00
parent 69bad7bf8c
commit 2d1fb52b2f
No known key found for this signature in database
GPG Key ID: C8CC613427A31C99
8 changed files with 85 additions and 53 deletions

View File

@ -26,7 +26,7 @@ namespace dxvk {
m_device (pDevice->GetDXVKDevice()),
m_context (m_device->createContext()),
m_frameLatencyCap(pDevice->GetOptions()->maxFrameLatency) {
CreateFrameLatencySignals();
CreateFrameLatencySignal();
if (!pDevice->GetOptions()->deferSurfaceCreation)
CreatePresenter();
@ -209,9 +209,8 @@ namespace dxvk {
immediateContext->Flush();
// Wait for the sync event so that we respect the maximum frame latency
uint32_t frameId = m_frameId++ % GetActualFrameLatency();
auto syncEvent = m_frameLatencySignals[frameId];
syncEvent->wait();
uint64_t frameId = ++m_frameId;
m_frameLatencySignal->wait(frameId - GetActualFrameLatency());
if (m_hud != nullptr)
m_hud->update();
@ -312,7 +311,7 @@ namespace dxvk {
m_hud->render(m_context, info.imageExtent);
if (i + 1 >= SyncInterval)
m_context->queueSignal(syncEvent);
m_context->signal(m_frameLatencySignal, frameId);
SubmitPresent(immediateContext, sync);
}
@ -370,9 +369,8 @@ namespace dxvk {
}
void D3D11SwapChain::CreateFrameLatencySignals() {
for (uint32_t i = 0; i < m_frameLatencySignals.size(); i++)
m_frameLatencySignals[i] = new sync::Signal(true);
void D3D11SwapChain::CreateFrameLatencySignal() {
m_frameLatencySignal = new sync::Fence(m_frameId);
}

View File

@ -117,9 +117,9 @@ namespace dxvk {
std::vector<Rc<DxvkImageView>> m_imageViews;
uint32_t m_frameId = 0;
std::array<Rc<sync::Signal>, DXGI_MAX_SWAP_CHAIN_BUFFERS> m_frameLatencySignals;
uint32_t m_frameLatencyCap = 0;
uint64_t m_frameId = DXGI_MAX_SWAP_CHAIN_BUFFERS;
uint32_t m_frameLatencyCap = 0;
Rc<sync::Signal> m_frameLatencySignal;
bool m_dirty = true;
bool m_vsync = true;
@ -135,7 +135,7 @@ namespace dxvk {
void RecreateSwapChain(
BOOL Vsync);
void CreateFrameLatencySignals();
void CreateFrameLatencySignal();
void CreatePresenter();

View File

@ -186,9 +186,11 @@ namespace dxvk {
*
* The signal will be notified once the command
* buffer has finished executing on the GPU.
* \param [in] signal The signal
* \param [in] value Signal value
*/
void queueSignal(const Rc<sync::Signal>& signal) {
m_signalTracker.add(signal);
void queueSignal(const Rc<sync::Signal>& signal, uint64_t value) {
m_signalTracker.add(signal, value);
}
/**

View File

@ -2375,8 +2375,8 @@ namespace dxvk {
}
void DxvkContext::queueSignal(const Rc<sync::Signal>& signal) {
m_cmd->queueSignal(signal);
void DxvkContext::signal(const Rc<sync::Signal>& signal, uint64_t value) {
m_cmd->queueSignal(signal, value);
}

View File

@ -992,9 +992,11 @@ namespace dxvk {
* previously submitted commands have
* finished execution on the GPU.
* \param [in] signal The signal
* \param [in] value Signal value
*/
void queueSignal(
const Rc<sync::Signal>& signal);
void signal(
const Rc<sync::Signal>& signal,
uint64_t value);
/**
* \brief Trims staging buffers

View File

@ -12,14 +12,14 @@ namespace dxvk {
}
void DxvkSignalTracker::add(const Rc<sync::Signal>& signal) {
m_signals.push_back(signal);
void DxvkSignalTracker::add(const Rc<sync::Signal>& signal, uint64_t value) {
m_signals.push_back({ signal, value });
}
void DxvkSignalTracker::notify() {
for (const auto& sig : m_signals)
sig->notify();
for (const auto& pair : m_signals)
pair.first->signal(pair.second);
}

View File

@ -16,9 +16,11 @@ namespace dxvk {
/**
* \brief Adds a signal to track
*
* \param [in] signal The signal
* \param [in] value Target value
*/
void add(const Rc<sync::Signal>& signal);
void add(const Rc<sync::Signal>& signal, uint64_t value);
/**
* \brief Notifies tracked signals
@ -32,7 +34,7 @@ namespace dxvk {
private:
std::vector<Rc<sync::Signal>> m_signals;
std::vector<std::pair<Rc<sync::Signal>, uint64_t>> m_signals;
};

View File

@ -9,52 +9,80 @@ namespace dxvk::sync {
/**
* \brief Signal
*
* Acts as a simple CPU fence which can be signaled by one
* thread and waited upon by one more thread. Waiting on
* more than one thread is not supported.
* Interface for a CPU-side fence. Can be signaled
* to a given value, and any thread waiting for a
* lower value will be woken up.
*/
class Signal : public RcObject {
public:
Signal()
: m_signaled(false) { }
Signal(bool signaled)
: m_signaled(signaled) { }
~Signal() { }
virtual ~Signal() { }
Signal (const Signal&) = delete;
Signal& operator = (const Signal&) = delete;
/**
* \brief Last signaled value
* \returns Last signaled value
*/
virtual uint64_t value() const = 0;
/**
* \brief Notifies signal
* Wakes any waiting thread.
*
* Wakes up all threads currently waiting for
* a value lower than \c value. Note that
* \c value must monotonically increase.
* \param [in] value Value to signal to
*/
void notify() {
std::lock_guard<std::mutex> lock(m_mutex);
m_signaled.store(true);
m_cond.notify_one();
}
virtual void signal(uint64_t value) = 0;
/**
* \brief Waits for signal
*
* Blocks the calling thread until another
* thread wakes it up, then resets it to
* the non-signaled state.
* thread signals it with a value equal to
* or greater than \c value.
* \param [in] value The value to wait for
*/
void wait() {
if (!m_signaled.exchange(false)) {
std::unique_lock<std::mutex> lock(m_mutex);
m_cond.wait(lock, [this] {
return m_signaled.exchange(false);
});
}
virtual void wait(uint64_t value) = 0;
};
/**
* \brief Fence
*
* Simple CPU-side fence.
*/
class Fence : public Signal {
public:
Fence()
: m_value(0ull) { }
explicit Fence(uint64_t value)
: m_value(value) { }
uint64_t value() const {
return m_value.load(std::memory_order_acquire);
}
void signal(uint64_t value) {
std::unique_lock<std::mutex> lock(m_mutex);
m_value.store(value, std::memory_order_release);
m_cond.notify_all();
}
void wait(uint64_t value) {
std::unique_lock<std::mutex> lock(m_mutex);
m_cond.wait(lock, [this, value] {
return value <= m_value.load(std::memory_order_acquire);
});
}
private:
std::atomic<bool> m_signaled;
std::atomic<uint64_t> m_value;
std::mutex m_mutex;
std::condition_variable m_cond;