diff options
| author | Nicolas James <Eele1Ephe7uZahRie@tutanota.com> | 2026-03-31 13:17:09 +1100 |
|---|---|---|
| committer | Nicolas James <Eele1Ephe7uZahRie@tutanota.com> | 2026-03-31 13:17:09 +1100 |
| commit | df2933fd9c0ea2a99e89a6837123dfdf8b549d4a (patch) | |
| tree | 42a5c73fc3735636efbe1eb7bf16adccd9aee664 /src | |
| parent | 89f4c0f59a90b1a4447d171bd09235126561af91 (diff) | |
Split monitoring strategy between Reflex and AL2
Diffstat (limited to 'src')
| -rw-r--r-- | src/device_context.cc | 6 | ||||
| -rw-r--r-- | src/device_context.hh | 3 | ||||
| -rw-r--r-- | src/layer.cc | 33 | ||||
| -rw-r--r-- | src/swapchain_monitor.cc | 59 | ||||
| -rw-r--r-- | src/swapchain_monitor.hh | 70 |
5 files changed, 120 insertions, 51 deletions
diff --git a/src/device_context.cc b/src/device_context.cc index c9f1fd5..b52fec4 100644 --- a/src/device_context.cc +++ b/src/device_context.cc @@ -37,14 +37,14 @@ void DeviceContext::update_params( // swapchains), just write it to everything. if (!target.has_value()) { for (auto& iter : this->swapchain_monitors) { - iter.second.update_params(was_low_latency_requested, present_delay); + iter.second->update_params(was_low_latency_requested, present_delay); } return; } const auto iter = this->swapchain_monitors.find(*target); assert(iter != std::end(this->swapchain_monitors)); - iter->second.update_params(was_low_latency_requested, present_delay); + iter->second->update_params(was_low_latency_requested, present_delay); } void DeviceContext::notify_present( @@ -54,7 +54,7 @@ void DeviceContext::notify_present( const auto iter = this->swapchain_monitors.find(swapchain); assert(iter != std::end(this->swapchain_monitors)); - iter->second.notify_present(submissions); + iter->second->notify_present(submissions); } } // namespace low_latency
\ No newline at end of file diff --git a/src/device_context.hh b/src/device_context.hh index 53970e5..a46f479 100644 --- a/src/device_context.hh +++ b/src/device_context.hh @@ -35,7 +35,8 @@ class DeviceContext final : public Context { std::unordered_map<VkQueue, std::shared_ptr<QueueContext>> queues; - std::unordered_map<VkSwapchainKHR, SwapchainMonitor> swapchain_monitors; + std::unordered_map<VkSwapchainKHR, std::unique_ptr<SwapchainMonitor>> + swapchain_monitors; public: DeviceContext(InstanceContext& parent_instance, diff --git a/src/layer.cc b/src/layer.cc index bc988f0..cf9f56e 100644 --- a/src/layer.cc +++ b/src/layer.cc @@ -20,6 +20,7 @@ #include "instance_context.hh" #include "layer_context.hh" #include "queue_context.hh" +#include "swapchain_monitor.hh" #include "timestamp_pool.hh" namespace low_latency { @@ -754,8 +755,17 @@ static VKAPI_ATTR VkResult VKAPI_CALL CreateSwapchainKHR( was_low_latency_requested = slci->latencyModeEnable; } - const auto [_, did_emplace] = context->swapchain_monitors.try_emplace( - *pSwapchain, *context, was_low_latency_requested); + auto insertion = [&]() -> std::unique_ptr<SwapchainMonitor> { + if (!layer_context.should_expose_reflex) { + return std::make_unique<AntiLagSwapchainMonitor>( + *context, was_low_latency_requested); + } + return std::make_unique<ReflexSwapchainMonitor>( + *context, was_low_latency_requested); + }(); + const auto did_emplace = context->swapchain_monitors + .try_emplace(*pSwapchain, std::move(insertion)) + .second; assert(did_emplace); return VK_SUCCESS; @@ -801,7 +811,13 @@ AntiLagUpdateAMD(VkDevice device, const VkAntiLagDataAMD* pData) { // and made sure that at least that one completed. I think it's more robust // to make sure they all complete. for (auto& iter : context->swapchain_monitors) { - iter.second.wait_until(); + + // All swapchains should be of type AntiLagSwapchainMonitor here. + const auto ptr = + dynamic_cast<AntiLagSwapchainMonitor*>(iter.second.get()); + assert(ptr); + + ptr->await_submissions(); } } @@ -813,16 +829,19 @@ VkResult LatencySleepNV(VkDevice device, VkSwapchainKHR swapchain, // We're associating an application-provided timeline semaphore + value with // a swapchain that says 'signal me when we should move past input'. - auto& swapchain_monitor = [&]() -> auto& { + auto swapchain_monitor_ptr = [&]() -> auto { const auto iter = context->swapchain_monitors.find(swapchain); assert(iter != std::end(context->swapchain_monitors)); - return iter->second; + const auto ptr = + dynamic_cast<ReflexSwapchainMonitor*>(iter->second.get()); + assert(ptr); + return ptr; }(); // Tell our swapchain monitor that if they want us to proceed they should // signal this semaphore. - swapchain_monitor.notify_semaphore(pSleepInfo->signalSemaphore, - pSleepInfo->value); + swapchain_monitor_ptr->notify_semaphore(pSleepInfo->signalSemaphore, + pSleepInfo->value); return VK_SUCCESS; } diff --git a/src/swapchain_monitor.cc b/src/swapchain_monitor.cc index bcf89e1..f12bafa 100644 --- a/src/swapchain_monitor.cc +++ b/src/swapchain_monitor.cc @@ -11,13 +11,27 @@ namespace low_latency { SwapchainMonitor::SwapchainMonitor(const DeviceContext& device, const bool was_low_latency_requested) - : device(device), was_low_latency_requested(was_low_latency_requested), - swapchain_worker( - std::bind_front(&SwapchainMonitor::do_swapchain_monitor, this)) {} + : device(device), was_low_latency_requested(was_low_latency_requested) {} SwapchainMonitor::~SwapchainMonitor() {} -void SwapchainMonitor::WakeupSemaphore::signal( +void SwapchainMonitor::update_params( + const bool was_low_latency_requested, + const std::chrono::milliseconds present_delay) { + + this->was_low_latency_requested = was_low_latency_requested; + this->present_delay = present_delay; +} + +ReflexSwapchainMonitor::ReflexSwapchainMonitor( + const DeviceContext& device, const bool was_low_latency_requested) + : SwapchainMonitor(device, was_low_latency_requested), + monitor_worker( + std::bind_front(&ReflexSwapchainMonitor::do_monitor, this)) {} + +ReflexSwapchainMonitor::~ReflexSwapchainMonitor() {} + +void ReflexSwapchainMonitor::WakeupSemaphore::signal( const DeviceContext& device) const { const auto ssi = @@ -27,7 +41,7 @@ void SwapchainMonitor::WakeupSemaphore::signal( THROW_NOT_VKSUCCESS(device.vtable.SignalSemaphore(device.device, &ssi)); } -void SwapchainMonitor::do_swapchain_monitor(const std::stop_token stoken) { +void ReflexSwapchainMonitor::do_monitor(const std::stop_token stoken) { for (;;) { auto lock = std::unique_lock{this->mutex}; this->cv.wait(lock, stoken, @@ -59,18 +73,8 @@ void SwapchainMonitor::do_swapchain_monitor(const std::stop_token stoken) { } } -void SwapchainMonitor::update_params( - const bool was_low_latency_requested, - const std::chrono::milliseconds present_delay) { - - const auto lock = std::scoped_lock{this->mutex}; - - this->was_low_latency_requested = was_low_latency_requested; - this->present_delay = present_delay; -} - -void SwapchainMonitor::notify_semaphore(const VkSemaphore& timeline_semaphore, - const std::uint64_t& value) { +void ReflexSwapchainMonitor::notify_semaphore( + const VkSemaphore& timeline_semaphore, const std::uint64_t& value) { const auto lock = std::scoped_lock{this->mutex}; @@ -90,7 +94,7 @@ void SwapchainMonitor::notify_semaphore(const VkSemaphore& timeline_semaphore, this->cv.notify_one(); } -void SwapchainMonitor::notify_present( +void ReflexSwapchainMonitor::notify_present( const QueueContext::submissions_t& submissions) { const auto lock = std::scoped_lock{this->mutex}; @@ -114,8 +118,23 @@ void SwapchainMonitor::notify_present( this->cv.notify_one(); } -void SwapchainMonitor::wait_until() { - // No reason to lock when using VK_AMD_anti_lag. +AntiLagSwapchainMonitor::AntiLagSwapchainMonitor( + const DeviceContext& device, const bool was_low_latency_requested) + : SwapchainMonitor(device, was_low_latency_requested) {} + +AntiLagSwapchainMonitor::~AntiLagSwapchainMonitor() {} + +void AntiLagSwapchainMonitor::notify_present( + const QueueContext::submissions_t& submissions) { + + if (!this->was_low_latency_requested) { + return; + } + + this->in_flight_submissions.emplace_back(submissions); +} + +void AntiLagSwapchainMonitor::await_submissions() { if (this->in_flight_submissions.empty()) { return; } diff --git a/src/swapchain_monitor.hh b/src/swapchain_monitor.hh index be81d59..b993b83 100644 --- a/src/swapchain_monitor.hh +++ b/src/swapchain_monitor.hh @@ -1,7 +1,7 @@ #ifndef SWAPCHAIN_MONITOR_HH_ #define SWAPCHAIN_MONITOR_HH_ -// The purpose of this file is to provide a SwapchainMonitor class definition. +// The purpose of this file is to provide a SwapchainMonitor #include <vulkan/vulkan_core.h> @@ -17,17 +17,41 @@ namespace low_latency { class DeviceContext; -// A swapchain monitor's job is to provide asynchronous wakeups for threads -// which request low_latency once the previous presentation has completed. -// It does this by signalling a semaphore a la VK_NV_low_latency2. +// Abstract base class for swapchain completion monitoring. Both implementations +// currently have an option to frame pace, to disable low_latency mode +// (become a no-op), and must track in_flight_submissions to function. class SwapchainMonitor { - private: + protected: const DeviceContext& device; // Configurarable params for this swapchain. std::chrono::milliseconds present_delay = std::chrono::milliseconds{0}; bool was_low_latency_requested = false; + std::deque<QueueContext::submissions_t> in_flight_submissions; + + public: + SwapchainMonitor(const DeviceContext& device, + const bool was_low_latency_requested); + SwapchainMonitor(const SwapchainMonitor&) = delete; + SwapchainMonitor(SwapchainMonitor&&) = delete; + SwapchainMonitor operator=(const SwapchainMonitor&) = delete; + SwapchainMonitor operator=(SwapchainMonitor&&) = delete; + virtual ~SwapchainMonitor(); + + public: + void update_params(const bool was_low_latency_requested, + const std::chrono::milliseconds present_delay); + + public: + virtual void + notify_present(const QueueContext::submissions_t& submissions) = 0; +}; + +// Provides asynchronous monitoring of submissions and signalling of some +// timeline semaphore via a worker thread. +class ReflexSwapchainMonitor final : public SwapchainMonitor { + private: struct WakeupSemaphore { VkSemaphore timeline_semaphore; std::uint64_t value; @@ -36,36 +60,42 @@ class SwapchainMonitor { void signal(const DeviceContext& device) const; }; std::deque<WakeupSemaphore> wakeup_semaphores; - std::deque<QueueContext::submissions_t> in_flight_submissions; std::mutex mutex; std::condition_variable_any cv; - std::jthread swapchain_worker; + std::jthread monitor_worker; private: - void do_swapchain_monitor(const std::stop_token stoken); + void do_monitor(const std::stop_token stoken); public: - SwapchainMonitor(const DeviceContext& device, - const bool was_low_latency_requested); - SwapchainMonitor(const SwapchainMonitor&); - SwapchainMonitor(SwapchainMonitor&&); - SwapchainMonitor operator=(const SwapchainMonitor&); - SwapchainMonitor operator=(SwapchainMonitor&&); - ~SwapchainMonitor(); + ReflexSwapchainMonitor(const DeviceContext& device, + const bool was_low_latency_requested); + virtual ~ReflexSwapchainMonitor(); public: - void update_params(const bool was_low_latency_requested, - const std::chrono::milliseconds present_delay); - void notify_semaphore(const VkSemaphore& timeline_semaphore, const std::uint64_t& value); - void notify_present(const QueueContext::submissions_t& submissions); + public: + virtual void + notify_present(const QueueContext::submissions_t& submissions) override; +}; + +// Much simpler synchronous waiting with no thread requirement. +class AntiLagSwapchainMonitor final : public SwapchainMonitor { + public: + AntiLagSwapchainMonitor(const DeviceContext& device, + const bool was_low_latency_requested); + virtual ~AntiLagSwapchainMonitor(); public: // Synchronously wait until all in-flight submissions have completed. - void wait_until(); + void await_submissions(); + + public: + virtual void + notify_present(const QueueContext::submissions_t& submissions) override; }; } // namespace low_latency |
