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:
parent
69bad7bf8c
commit
2d1fb52b2f
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
@ -9,55 +9,83 @@ 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() { }
|
||||
|
||||
Signal (const Signal&) = delete;
|
||||
Signal& operator = (const Signal&) = delete;
|
||||
virtual ~Signal() { }
|
||||
|
||||
/**
|
||||
* \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;
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user