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_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);
} }

View File

@ -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();

View File

@ -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);
} }
/** /**

View File

@ -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);
} }

View File

@ -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

View File

@ -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);
} }

View File

@ -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;
}; };

View File

@ -9,55 +9,83 @@ 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) { } * \brief Last signaled value
~Signal() { } * \returns Last signaled value
*/
Signal (const Signal&) = delete; virtual uint64_t value() const = 0;
Signal& operator = (const Signal&) = delete;
/** /**
* \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;
}; };
} }