#pragma once #include #include "sync_signal.h" namespace dxvk::sync { /** * \brief Win32 fence * * CPU-side fence that also has the * ability to signal Win32 events. */ class Win32Fence final : public Signal { public: Win32Fence() : m_value(0ull) { } explicit Win32Fence(uint64_t value) : m_value(value) { } /** * \brief Last signaled value * \returns Last signaled value */ uint64_t value() const { return m_value.load(std::memory_order_acquire); } /** * \brief Notifies signal * * In addition to waking up blocked threads, this * will also signal any queued win32 events with * a lower or equal value. * \param [in] value Value to set signal to */ void signal(uint64_t value) { std::unique_lock lock(m_mutex); m_value.store(value, std::memory_order_release); m_cond.notify_all(); for (auto i = m_events.begin(); i != m_events.end(); ) { if (value >= i->second) { SetEvent(i->first); i = m_events.erase(i); } else { i++; } } } /** * \brief Waits for signal * \param [in] value The value to wait for */ void wait(uint64_t value) { std::unique_lock lock(m_mutex); m_cond.wait(lock, [this, value] { return value <= m_value.load(std::memory_order_acquire); }); } /** * \brief Sets Win32 event on completion * * When the signal gets signaled with a value equal to or * greater than the given value, the event will be signaled. * Signals the event immediately if the last signaled value * is already greater than or equal to the requested value. * \param [in] event Win32 Event to signal * \param [in] value Requested signal value */ void setEvent(HANDLE event, uint64_t value) { std::unique_lock lock(m_mutex); if (value > this->value()) m_events.push_back({ event, value }); else SetEvent(event); } private: std::atomic m_value; std::mutex m_mutex; std::condition_variable m_cond; std::list> m_events; }; }