diff options
| author | Nicolas James <nj3ahxac@gmail.com> | 2026-04-06 12:18:10 +1000 |
|---|---|---|
| committer | Nicolas James <nj3ahxac@gmail.com> | 2026-04-06 12:18:10 +1000 |
| commit | a9a083ea5c649498d2f12e611dbc7c767d152130 (patch) | |
| tree | e67cfd11bc37a4faa0f1fbd448e66307cd75a624 /src/strategies/low_latency2 | |
| parent | fcdac1c3287d314d7127516d56f0dec788392063 (diff) | |
Add WIP refactored reflex impl
Diffstat (limited to 'src/strategies/low_latency2')
| -rw-r--r-- | src/strategies/low_latency2/device_strategy.cc | 31 | ||||
| -rw-r--r-- | src/strategies/low_latency2/device_strategy.hh | 16 | ||||
| -rw-r--r-- | src/strategies/low_latency2/queue_strategy.cc | 26 | ||||
| -rw-r--r-- | src/strategies/low_latency2/queue_strategy.hh | 1 | ||||
| -rw-r--r-- | src/strategies/low_latency2/swapchain_monitor.cc | 104 | ||||
| -rw-r--r-- | src/strategies/low_latency2/swapchain_monitor.hh | 72 |
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 |
