aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/atomic_time_point.cc27
-rw-r--r--src/atomic_time_point.hh35
-rw-r--r--src/strategies/anti_lag/device_strategy.cc20
-rw-r--r--src/strategies/anti_lag/device_strategy.hh6
-rw-r--r--src/strategies/low_latency2/swapchain_monitor.cc29
-rw-r--r--src/strategies/low_latency2/swapchain_monitor.hh5
6 files changed, 93 insertions, 29 deletions
diff --git a/src/atomic_time_point.cc b/src/atomic_time_point.cc
new file mode 100644
index 0000000..4aaef7f
--- /dev/null
+++ b/src/atomic_time_point.cc
@@ -0,0 +1,27 @@
+#include "atomic_time_point.hh"
+
+#include <cassert>
+
+namespace low_latency {
+
+AtomicTimePoint::AtomicTimePoint() {}
+
+AtomicTimePoint::~AtomicTimePoint() {}
+
+bool AtomicTimePoint::has_value() const {
+ return this->count.load(std::memory_order_relaxed);
+}
+
+std::chrono::steady_clock::time_point AtomicTimePoint::get() const {
+ const auto result = this->count.load(std::memory_order_relaxed);
+ assert(result);
+ using namespace std::chrono;
+ return steady_clock::time_point{steady_clock::duration{result}};
+}
+
+void AtomicTimePoint::set(const std::chrono::steady_clock::time_point target) {
+ this->count.store(target.time_since_epoch().count(),
+ std::memory_order_relaxed);
+}
+
+} // namespace low_latency \ No newline at end of file
diff --git a/src/atomic_time_point.hh b/src/atomic_time_point.hh
new file mode 100644
index 0000000..13e62aa
--- /dev/null
+++ b/src/atomic_time_point.hh
@@ -0,0 +1,35 @@
+#ifndef ATOMIC_TIME_POINT_HH_
+#define ATOMIC_TIME_POINT_HH_
+
+#include <atomic>
+#include <chrono>
+
+// The purpose of this class is to provide a simple time point which may be read
+// from atomically and without locks.
+
+namespace low_latency {
+
+class AtomicTimePoint final {
+ private:
+ std::atomic<std::int64_t> count{};
+ static_assert(decltype(count)::is_always_lock_free);
+
+ public:
+ AtomicTimePoint();
+ AtomicTimePoint(const AtomicTimePoint&) = delete;
+ AtomicTimePoint(AtomicTimePoint&&) = delete;
+ AtomicTimePoint operator=(const AtomicTimePoint&) = delete;
+ AtomicTimePoint operator=(AtomicTimePoint&&) = delete;
+ ~AtomicTimePoint();
+
+ public:
+ bool has_value() const;
+
+ std::chrono::steady_clock::time_point get() const;
+
+ void set(const std::chrono::steady_clock::time_point target);
+};
+
+} // namespace low_latency
+
+#endif \ No newline at end of file
diff --git a/src/strategies/anti_lag/device_strategy.cc b/src/strategies/anti_lag/device_strategy.cc
index 2d1d9ad..c0ab882 100644
--- a/src/strategies/anti_lag/device_strategy.cc
+++ b/src/strategies/anti_lag/device_strategy.cc
@@ -19,7 +19,7 @@ void AntiLagDeviceStrategy::notify_update(const VkAntiLagDataAMD& data) {
this->is_enabled = !(data.mode == VK_ANTI_LAG_MODE_OFF_AMD);
- this->delay = [&]() -> std::chrono::microseconds {
+ this->input_delay = [&]() -> std::chrono::microseconds {
using namespace std::chrono;
if (!data.maxFPS) {
return 0us;
@@ -40,7 +40,10 @@ void AntiLagDeviceStrategy::notify_update(const VkAntiLagDataAMD& data) {
// If we're at the input stage, start marking submissions as relevant.
this->frame_index.emplace(data.pPresentationInfo->frameIndex);
+ // Grab this before we unlock the mutex.
+ const auto delay = this->input_delay;
lock.unlock();
+
// We need to collect all queue submission and wait on them in this thread.
// Input stage needs to wait for all queue submissions to complete.
const auto queue_frame_spans = [&]() -> auto {
@@ -61,25 +64,20 @@ void AntiLagDeviceStrategy::notify_update(const VkAntiLagDataAMD& data) {
return queue_frame_spans;
}();
- // Wait on them and relock the mutex.
+ // Wait on outstanding work to complete.
for (const auto& frame_span : queue_frame_spans) {
if (frame_span) { // Can still be null here.
frame_span->await_completed();
}
}
- lock.lock();
-
// We might need to wait a little more time to meet our frame limit.
using namespace std::chrono;
- if (this->delay != 0us && this->previous_input_release.has_value()) {
- lock.unlock();
- std::this_thread::sleep_until(*this->previous_input_release +
- this->delay);
- lock.lock();
+ if (delay != 0us && this->previous_input_release.has_value()) {
+ const auto last = this->previous_input_release.get();
+ std::this_thread::sleep_until(last + delay);
}
-
- this->previous_input_release = steady_clock::now();
+ this->previous_input_release.set(steady_clock::now());
}
bool AntiLagDeviceStrategy::should_track_submissions() {
diff --git a/src/strategies/anti_lag/device_strategy.hh b/src/strategies/anti_lag/device_strategy.hh
index 3533647..9ff1212 100644
--- a/src/strategies/anti_lag/device_strategy.hh
+++ b/src/strategies/anti_lag/device_strategy.hh
@@ -1,6 +1,7 @@
#ifndef STRATEGIES_ANTI_LAG_DEVICE_STRATEGY_HH_
#define STRATEGIES_ANTI_LAG_DEVICE_STRATEGY_HH_
+#include "atomic_time_point.hh"
#include "strategies/device_strategy.hh"
#include <vulkan/vulkan.h>
@@ -18,10 +19,11 @@ class AntiLagDeviceStrategy final : public DeviceStrategy {
std::shared_mutex mutex{};
// If this is nullopt don't track the submission.
std::optional<std::uint64_t> frame_index{};
- std::optional<std::chrono::steady_clock::time_point> previous_input_release{};
- std::chrono::microseconds delay{};
+ std::chrono::microseconds input_delay{};
bool is_enabled{};
+ AtomicTimePoint previous_input_release;
+
public:
AntiLagDeviceStrategy(DeviceContext& device);
virtual ~AntiLagDeviceStrategy();
diff --git a/src/strategies/low_latency2/swapchain_monitor.cc b/src/strategies/low_latency2/swapchain_monitor.cc
index 3d1d276..c2a328b 100644
--- a/src/strategies/low_latency2/swapchain_monitor.cc
+++ b/src/strategies/low_latency2/swapchain_monitor.cc
@@ -53,28 +53,26 @@ void SwapchainMonitor::do_monitor(const std::stop_token stoken) {
break;
}
- // Unlock, wait for work to finish, lock again.
+ // Grab mutex protected present delay before we sleep - doesn't matter
+ // if it's 'old'.
+ const auto delay = this->present_delay;
lock.unlock();
+
+ // Wait for work to complete.
for (const auto& frame_span : pending_signal.frame_spans) {
if (frame_span) {
frame_span->await_completed();
}
}
- lock.lock();
+ // Wait for possible need to delay the frame.
using namespace std::chrono;
- if (this->present_delay != 0us) {
- const auto last_time = this->last_signal_time;
- const auto delay = this->present_delay;
- if (last_time.has_value()) {
- lock.unlock();
- std::this_thread::sleep_until(*last_time + delay);
- lock.lock();
- }
- this->last_signal_time.emplace(steady_clock::now());
+ if (delay != 0us && this->last_signal_time.has_value()) {
+ const auto last = this->last_signal_time.get();
+ std::this_thread::sleep_until(last + delay);
}
- lock.unlock();
+ this->last_signal_time.set(std::chrono::steady_clock::now());
pending_signal.wakeup_semaphore.signal(this->device);
}
}
@@ -93,8 +91,11 @@ void SwapchainMonitor::notify_semaphore(const VkSemaphore& timeline_semaphore,
return;
}
- // Signal immediately if we have no outstanding work.
- if (std::ranges::all_of(this->pending_frame_spans,
+ // Signal immediately if we don't need to worry about delaying the frame and
+ // we have no outstanding work.
+ using namespace std::chrono;
+ if (this->present_delay == 0us &&
+ std::ranges::all_of(this->pending_frame_spans,
[](const auto& frame_span) {
if (!frame_span) {
return true;
diff --git a/src/strategies/low_latency2/swapchain_monitor.hh b/src/strategies/low_latency2/swapchain_monitor.hh
index a5f8362..28771cf 100644
--- a/src/strategies/low_latency2/swapchain_monitor.hh
+++ b/src/strategies/low_latency2/swapchain_monitor.hh
@@ -2,8 +2,10 @@
#ifndef SWAPCHAIN_MONITOR_HH_
#define SWAPCHAIN_MONITOR_HH_
+#include "atomic_time_point.hh"
#include "frame_span.hh"
+#include <atomic>
#include <vulkan/vulkan.h>
#include <chrono>
@@ -40,12 +42,11 @@ class SwapchainMonitor final {
std::mutex mutex{};
std::chrono::microseconds present_delay{};
bool was_low_latency_requested{};
+ AtomicTimePoint last_signal_time{};
std::condition_variable_any cv{};
std::jthread monitor_worker{};
- std::optional<std::chrono::steady_clock::time_point> last_signal_time;
-
void do_monitor(const std::stop_token stoken);
public: