aboutsummaryrefslogtreecommitdiff
path: root/src/strategies/low_latency2
diff options
context:
space:
mode:
authorNicolas James <nj3ahxac@gmail.com>2026-04-06 12:18:10 +1000
committerNicolas James <nj3ahxac@gmail.com>2026-04-06 12:18:10 +1000
commita9a083ea5c649498d2f12e611dbc7c767d152130 (patch)
treee67cfd11bc37a4faa0f1fbd448e66307cd75a624 /src/strategies/low_latency2
parentfcdac1c3287d314d7127516d56f0dec788392063 (diff)
Add WIP refactored reflex impl
Diffstat (limited to 'src/strategies/low_latency2')
-rw-r--r--src/strategies/low_latency2/device_strategy.cc31
-rw-r--r--src/strategies/low_latency2/device_strategy.hh16
-rw-r--r--src/strategies/low_latency2/queue_strategy.cc26
-rw-r--r--src/strategies/low_latency2/queue_strategy.hh1
-rw-r--r--src/strategies/low_latency2/swapchain_monitor.cc104
-rw-r--r--src/strategies/low_latency2/swapchain_monitor.hh72
6 files changed, 250 insertions, 0 deletions
diff --git a/src/strategies/low_latency2/device_strategy.cc b/src/strategies/low_latency2/device_strategy.cc
index 7c10088..3a970a2 100644
--- a/src/strategies/low_latency2/device_strategy.cc
+++ b/src/strategies/low_latency2/device_strategy.cc
@@ -1,5 +1,8 @@
#include "device_strategy.hh"
+#include "helper.hh"
+#include <mutex>
+
namespace low_latency {
LowLatency2DeviceStrategy::LowLatency2DeviceStrategy(DeviceContext& device)
@@ -7,4 +10,32 @@ LowLatency2DeviceStrategy::LowLatency2DeviceStrategy(DeviceContext& device)
LowLatency2DeviceStrategy::~LowLatency2DeviceStrategy() {}
+void LowLatency2DeviceStrategy::notify_create_swapchain(
+ const VkSwapchainKHR& swapchain, const VkSwapchainCreateInfoKHR& info) {
+
+ // VK_NV_low_latency2 allows a swapchain to be created with the low latency
+ // mode already on via VkSwapchainLatencyCreateInfoNV.
+ auto was_low_latency_requested = bool{false};
+ if (const auto slci = find_next<VkSwapchainLatencyCreateInfoNV>(
+ &info, VK_STRUCTURE_TYPE_SWAPCHAIN_LATENCY_CREATE_INFO_NV);
+ slci) {
+
+ was_low_latency_requested = slci->latencyModeEnable;
+ }
+
+ const auto lock = std::scoped_lock{this->mutex};
+ const auto iter = this->swapchain_monitors.emplace(swapchain, this->device);
+ assert(iter.second);
+ iter.first->second.update_params(was_low_latency_requested,
+ std::chrono::microseconds{0});
+}
+
+void LowLatency2DeviceStrategy::notify_destroy_swapchain(
+ const VkSwapchainKHR& swapchain) {
+
+ const auto lock = std::scoped_lock{this->mutex};
+
+ this->swapchain_monitors.erase(swapchain);
+}
+
} // namespace low_latency
diff --git a/src/strategies/low_latency2/device_strategy.hh b/src/strategies/low_latency2/device_strategy.hh
index 18f8bd9..af1b471 100644
--- a/src/strategies/low_latency2/device_strategy.hh
+++ b/src/strategies/low_latency2/device_strategy.hh
@@ -2,15 +2,31 @@
#define STRATEGIES_LOW_LATENCY2_DEVICE_STRATEGY_HH_
#include "strategies/device_strategy.hh"
+#include "swapchain_monitor.hh"
+
+#include <shared_mutex>
+#include <unordered_map>
namespace low_latency {
class DeviceContext;
class LowLatency2DeviceStrategy final : public DeviceStrategy {
+ private:
+ std::shared_mutex mutex;
+ // swapchain -> swapchain monitor
+ std::unordered_map<VkSwapchainKHR, SwapchainMonitor> swapchain_monitors;
+
public:
LowLatency2DeviceStrategy(DeviceContext& device);
virtual ~LowLatency2DeviceStrategy();
+
+ public:
+ virtual void
+ notify_create_swapchain(const VkSwapchainKHR& swapchain,
+ const VkSwapchainCreateInfoKHR& info) override;
+ virtual void
+ notify_destroy_swapchain(const VkSwapchainKHR& swapchain) override;
};
} // namespace low_latency
diff --git a/src/strategies/low_latency2/queue_strategy.cc b/src/strategies/low_latency2/queue_strategy.cc
index e67d279..855ff5d 100644
--- a/src/strategies/low_latency2/queue_strategy.cc
+++ b/src/strategies/low_latency2/queue_strategy.cc
@@ -1,4 +1,8 @@
#include "queue_strategy.hh"
+#include "helper.hh"
+
+#include <ranges>
+#include <span>
namespace low_latency {
@@ -15,4 +19,26 @@ void LowLatency2QueueStrategy::notify_submit(
[[maybe_unused]] const VkSubmitInfo2& submit,
[[maybe_unused]] std::unique_ptr<Submission> submission) {}
+void LowLatency2QueueStrategy::notify_present(const VkPresentInfoKHR& present) {
+
+ const auto pid =
+ find_next<VkPresentIdKHR>(&present, VK_STRUCTURE_TYPE_PRESENT_ID_KHR);
+
+ // All submissions should be tagged with a present_id. If it isn't, I'm not
+ // going to fail hard here - we will just ignore it.
+ if (!pid) {
+ return;
+ }
+
+ const auto swapchains =
+ std::span{present.pSwapchains, present.swapchainCount};
+ const auto present_ids =
+ std::span{pid->pPresentIds, present.swapchainCount};
+ for (const auto& [swapchain, present_id] :
+ std::views::zip(swapchains, present_ids)) {
+
+ // TODO
+ }
+}
+
} // namespace low_latency
diff --git a/src/strategies/low_latency2/queue_strategy.hh b/src/strategies/low_latency2/queue_strategy.hh
index ad31df4..223f559 100644
--- a/src/strategies/low_latency2/queue_strategy.hh
+++ b/src/strategies/low_latency2/queue_strategy.hh
@@ -17,6 +17,7 @@ class LowLatency2QueueStrategy final : public QueueStrategy {
std::unique_ptr<Submission> submission) override;
virtual void notify_submit(const VkSubmitInfo2& submit,
std::unique_ptr<Submission> submission) override;
+ virtual void notify_present(const VkPresentInfoKHR& present) override;
};
} // namespace low_latency
diff --git a/src/strategies/low_latency2/swapchain_monitor.cc b/src/strategies/low_latency2/swapchain_monitor.cc
new file mode 100644
index 0000000..3c9b5e7
--- /dev/null
+++ b/src/strategies/low_latency2/swapchain_monitor.cc
@@ -0,0 +1,104 @@
+#include "swapchain_monitor.hh"
+#include "device_context.hh"
+#include "helper.hh"
+
+namespace low_latency {
+
+void SwapchainMonitor::WakeupSemaphore::signal(
+ const DeviceContext& device) const {
+
+ const auto ssi =
+ VkSemaphoreSignalInfo{.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO,
+ .semaphore = this->timeline_semaphore,
+ .value = this->value};
+ THROW_NOT_VKSUCCESS(device.vtable.SignalSemaphore(device.device, &ssi));
+}
+
+void SwapchainMonitor::update_params(const bool was_low_latency_requested,
+ const std::chrono::microseconds delay) {
+
+ const auto lock = std::scoped_lock{this->mutex};
+
+ this->was_low_latency_requested = was_low_latency_requested;
+ this->present_delay = delay;
+}
+
+void SwapchainMonitor::do_monitor(const std::stop_token stoken) {
+ for (;;) {
+ auto lock = std::unique_lock{this->mutex};
+ this->cv.wait(lock, stoken,
+ [&]() { return this->semaphore_submission.has_value(); });
+
+ // Stop only if we're stopped and we have nothing to signal.
+ if (stoken.stop_requested() &&
+ !this->semaphore_submission.has_value()) {
+ break;
+ }
+
+ // Grab the most recent semaphore. When work completes, signal it.
+ const auto semaphore_submission =
+ std::move(*this->semaphore_submission);
+ this->semaphore_submission.reset();
+
+ // If we're stopping, signal the semaphore and don't worry about work
+ // actually completing.
+ if (stoken.stop_requested()) {
+ semaphore_submission.wakeup_semaphore.signal(this->device);
+ break;
+ }
+
+ // Unlock, wait for work to finish, signal semaphore.
+ lock.unlock();
+ // Ugly and duplicated - will fix this soon.
+ if (!semaphore_submission.submissions->empty()) {
+ semaphore_submission.submissions->back().end->await_time();
+ }
+
+ // TODO add wait for frame pacing
+ semaphore_submission.wakeup_semaphore.signal(this->device);
+ }
+}
+
+void SwapchainMonitor::notify_semaphore(const VkSemaphore& timeline_semaphore,
+ const std::uint64_t& value) {
+
+ auto lock = std::unique_lock{this->mutex};
+
+ const auto wakeup_semaphore = WakeupSemaphore{
+ .timeline_semaphore = timeline_semaphore, .value = value};
+
+ // Signal immediately if reflex is off or it's a no-op submit.
+ if (!this->was_low_latency_requested) {
+ wakeup_semaphore.signal(this->device);
+ return;
+ }
+
+ // Signal immediately if we have no outstanding work.
+ if (!this->pending_submissions) {
+ this->pending_submissions.reset();
+ wakeup_semaphore.signal(this->device);
+ return;
+ }
+
+ this->semaphore_submission.emplace(SemaphoreSubmissions{
+ .wakeup_semaphore = wakeup_semaphore,
+ .submissions = std::move(this->pending_submissions),
+ });
+ this->pending_submissions.reset();
+
+ lock.unlock();
+ this->cv.notify_one();
+}
+
+void SwapchainMonitor::attach_work(
+ std::unique_ptr<std::deque<Submission>> submissions) {
+
+ const auto lock = std::scoped_lock{this->mutex};
+ if (!this->was_low_latency_requested) {
+ return;
+ }
+
+ this->pending_submissions = std::move(submissions);
+}
+
+} // namespace low_latency \ No newline at end of file
diff --git a/src/strategies/low_latency2/swapchain_monitor.hh b/src/strategies/low_latency2/swapchain_monitor.hh
new file mode 100644
index 0000000..9031bbb
--- /dev/null
+++ b/src/strategies/low_latency2/swapchain_monitor.hh
@@ -0,0 +1,72 @@
+
+#ifndef SWAPCHAIN_MONITOR_HH_
+#define SWAPCHAIN_MONITOR_HH_
+
+#include <vulkan/vulkan.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <deque>
+#include <memory>
+#include <mutex>
+#include <thread>
+
+#include "submission.hh"
+
+namespace low_latency {
+
+class DeviceContext;
+
+class SwapchainMonitor final {
+ private:
+ struct WakeupSemaphore {
+ VkSemaphore timeline_semaphore{};
+ std::uint64_t value{};
+
+ public:
+ void signal(const DeviceContext& device) const;
+ };
+
+ std::unique_ptr<std::deque<Submission>> pending_submissions{};
+
+ // A pairing of semaphore -> submissions.
+ // If the Submissions completes then signal the bundled semaphore.
+ struct SemaphoreSubmissions {
+ WakeupSemaphore wakeup_semaphore{};
+ std::unique_ptr<std::deque<Submission>> submissions{};
+ };
+ std::optional<SemaphoreSubmissions> semaphore_submission{};
+
+ protected:
+ const DeviceContext& device;
+
+ std::mutex mutex{};
+ std::chrono::microseconds present_delay{};
+ bool was_low_latency_requested{};
+
+ std::condition_variable_any cv{};
+ std::jthread monitor_worker{};
+
+ void do_monitor(const std::stop_token stoken);
+
+ public:
+ SwapchainMonitor(const DeviceContext& device);
+ SwapchainMonitor(const SwapchainMonitor&) = delete;
+ SwapchainMonitor(SwapchainMonitor&&) = delete;
+ SwapchainMonitor operator=(const SwapchainMonitor&) = delete;
+ SwapchainMonitor operator=(SwapchainMonitor&&) = delete;
+ ~SwapchainMonitor();
+
+ public:
+ void update_params(const bool was_low_latency_requested,
+ const std::chrono::microseconds delay);
+
+ void notify_semaphore(const VkSemaphore& timeline_semaphore,
+ const std::uint64_t& value);
+
+ void attach_work(std::unique_ptr<std::deque<Submission>> submissions);
+};
+
+} // namespace low_latency
+
+#endif \ No newline at end of file