mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-01-19 05:52:11 +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_device (pDevice->GetDXVKDevice()),
|
||||||
m_context (m_device->createContext()),
|
m_context (m_device->createContext()),
|
||||||
m_frameLatencyCap(pDevice->GetOptions()->maxFrameLatency) {
|
m_frameLatencyCap(pDevice->GetOptions()->maxFrameLatency) {
|
||||||
CreateFrameLatencySignals();
|
CreateFrameLatencySignal();
|
||||||
|
|
||||||
if (!pDevice->GetOptions()->deferSurfaceCreation)
|
if (!pDevice->GetOptions()->deferSurfaceCreation)
|
||||||
CreatePresenter();
|
CreatePresenter();
|
||||||
@ -209,9 +209,8 @@ namespace dxvk {
|
|||||||
immediateContext->Flush();
|
immediateContext->Flush();
|
||||||
|
|
||||||
// Wait for the sync event so that we respect the maximum frame latency
|
// Wait for the sync event so that we respect the maximum frame latency
|
||||||
uint32_t frameId = m_frameId++ % GetActualFrameLatency();
|
uint64_t frameId = ++m_frameId;
|
||||||
auto syncEvent = m_frameLatencySignals[frameId];
|
m_frameLatencySignal->wait(frameId - GetActualFrameLatency());
|
||||||
syncEvent->wait();
|
|
||||||
|
|
||||||
if (m_hud != nullptr)
|
if (m_hud != nullptr)
|
||||||
m_hud->update();
|
m_hud->update();
|
||||||
@ -312,7 +311,7 @@ namespace dxvk {
|
|||||||
m_hud->render(m_context, info.imageExtent);
|
m_hud->render(m_context, info.imageExtent);
|
||||||
|
|
||||||
if (i + 1 >= SyncInterval)
|
if (i + 1 >= SyncInterval)
|
||||||
m_context->queueSignal(syncEvent);
|
m_context->signal(m_frameLatencySignal, frameId);
|
||||||
|
|
||||||
SubmitPresent(immediateContext, sync);
|
SubmitPresent(immediateContext, sync);
|
||||||
}
|
}
|
||||||
@ -370,9 +369,8 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void D3D11SwapChain::CreateFrameLatencySignals() {
|
void D3D11SwapChain::CreateFrameLatencySignal() {
|
||||||
for (uint32_t i = 0; i < m_frameLatencySignals.size(); i++)
|
m_frameLatencySignal = new sync::Fence(m_frameId);
|
||||||
m_frameLatencySignals[i] = new sync::Signal(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,9 +117,9 @@ namespace dxvk {
|
|||||||
|
|
||||||
std::vector<Rc<DxvkImageView>> m_imageViews;
|
std::vector<Rc<DxvkImageView>> m_imageViews;
|
||||||
|
|
||||||
uint32_t m_frameId = 0;
|
uint64_t m_frameId = DXGI_MAX_SWAP_CHAIN_BUFFERS;
|
||||||
std::array<Rc<sync::Signal>, DXGI_MAX_SWAP_CHAIN_BUFFERS> m_frameLatencySignals;
|
|
||||||
uint32_t m_frameLatencyCap = 0;
|
uint32_t m_frameLatencyCap = 0;
|
||||||
|
Rc<sync::Signal> m_frameLatencySignal;
|
||||||
|
|
||||||
bool m_dirty = true;
|
bool m_dirty = true;
|
||||||
bool m_vsync = true;
|
bool m_vsync = true;
|
||||||
@ -135,7 +135,7 @@ namespace dxvk {
|
|||||||
void RecreateSwapChain(
|
void RecreateSwapChain(
|
||||||
BOOL Vsync);
|
BOOL Vsync);
|
||||||
|
|
||||||
void CreateFrameLatencySignals();
|
void CreateFrameLatencySignal();
|
||||||
|
|
||||||
void CreatePresenter();
|
void CreatePresenter();
|
||||||
|
|
||||||
|
@ -186,9 +186,11 @@ namespace dxvk {
|
|||||||
*
|
*
|
||||||
* The signal will be notified once the command
|
* The signal will be notified once the command
|
||||||
* buffer has finished executing on the GPU.
|
* buffer has finished executing on the GPU.
|
||||||
|
* \param [in] signal The signal
|
||||||
|
* \param [in] value Signal value
|
||||||
*/
|
*/
|
||||||
void queueSignal(const Rc<sync::Signal>& signal) {
|
void queueSignal(const Rc<sync::Signal>& signal, uint64_t value) {
|
||||||
m_signalTracker.add(signal);
|
m_signalTracker.add(signal, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2375,8 +2375,8 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkContext::queueSignal(const Rc<sync::Signal>& signal) {
|
void DxvkContext::signal(const Rc<sync::Signal>& signal, uint64_t value) {
|
||||||
m_cmd->queueSignal(signal);
|
m_cmd->queueSignal(signal, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -992,9 +992,11 @@ namespace dxvk {
|
|||||||
* previously submitted commands have
|
* previously submitted commands have
|
||||||
* finished execution on the GPU.
|
* finished execution on the GPU.
|
||||||
* \param [in] signal The signal
|
* \param [in] signal The signal
|
||||||
|
* \param [in] value Signal value
|
||||||
*/
|
*/
|
||||||
void queueSignal(
|
void signal(
|
||||||
const Rc<sync::Signal>& signal);
|
const Rc<sync::Signal>& signal,
|
||||||
|
uint64_t value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Trims staging buffers
|
* \brief Trims staging buffers
|
||||||
|
@ -12,14 +12,14 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkSignalTracker::add(const Rc<sync::Signal>& signal) {
|
void DxvkSignalTracker::add(const Rc<sync::Signal>& signal, uint64_t value) {
|
||||||
m_signals.push_back(signal);
|
m_signals.push_back({ signal, value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkSignalTracker::notify() {
|
void DxvkSignalTracker::notify() {
|
||||||
for (const auto& sig : m_signals)
|
for (const auto& pair : m_signals)
|
||||||
sig->notify();
|
pair.first->signal(pair.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,9 +16,11 @@ namespace dxvk {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Adds a signal to track
|
* \brief Adds a signal to track
|
||||||
|
*
|
||||||
* \param [in] signal The signal
|
* \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
|
* \brief Notifies tracked signals
|
||||||
@ -32,7 +34,7 @@ namespace dxvk {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::vector<Rc<sync::Signal>> m_signals;
|
std::vector<std::pair<Rc<sync::Signal>, uint64_t>> m_signals;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,52 +9,80 @@ namespace dxvk::sync {
|
|||||||
/**
|
/**
|
||||||
* \brief Signal
|
* \brief Signal
|
||||||
*
|
*
|
||||||
* Acts as a simple CPU fence which can be signaled by one
|
* Interface for a CPU-side fence. Can be signaled
|
||||||
* thread and waited upon by one more thread. Waiting on
|
* to a given value, and any thread waiting for a
|
||||||
* more than one thread is not supported.
|
* lower value will be woken up.
|
||||||
*/
|
*/
|
||||||
class Signal : public RcObject {
|
class Signal : public RcObject {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Signal()
|
virtual ~Signal() { }
|
||||||
: m_signaled(false) { }
|
|
||||||
Signal(bool signaled)
|
|
||||||
: m_signaled(signaled) { }
|
|
||||||
~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
|
* \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() {
|
virtual void signal(uint64_t value) = 0;
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
|
||||||
m_signaled.store(true);
|
|
||||||
m_cond.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Waits for signal
|
* \brief Waits for signal
|
||||||
*
|
*
|
||||||
* Blocks the calling thread until another
|
* Blocks the calling thread until another
|
||||||
* thread wakes it up, then resets it to
|
* thread signals it with a value equal to
|
||||||
* the non-signaled state.
|
* or greater than \c value.
|
||||||
|
* \param [in] value The value to wait for
|
||||||
*/
|
*/
|
||||||
void wait() {
|
virtual void wait(uint64_t value) = 0;
|
||||||
if (!m_signaled.exchange(false)) {
|
|
||||||
std::unique_lock<std::mutex> lock(m_mutex);
|
};
|
||||||
m_cond.wait(lock, [this] {
|
|
||||||
return m_signaled.exchange(false);
|
|
||||||
});
|
/**
|
||||||
|
* \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:
|
private:
|
||||||
|
|
||||||
std::atomic<bool> m_signaled;
|
std::atomic<uint64_t> m_value;
|
||||||
std::mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
std::condition_variable m_cond;
|
std::condition_variable m_cond;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user