aboutsummaryrefslogtreecommitdiff
path: root/src/strategies/low_latency2/swapchain_monitor.cc
blob: 3d1d2767c26ce4d3130f40da8a2ab349f8476fe9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include "swapchain_monitor.hh"
#include "device_context.hh"
#include "helper.hh"

#include <functional>

namespace low_latency {

SwapchainMonitor::SwapchainMonitor(const DeviceContext& device)
    : device(device),
      monitor_worker(std::bind_front(&SwapchainMonitor::do_monitor, this)) {}

SwapchainMonitor::~SwapchainMonitor() {}

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->pending_signals.empty(); });

        // Stop only if we're stopped and we have nothing to signal.
        if (stoken.stop_requested() && this->pending_signals.empty()) {
            break;
        }

        // Grab the most recent semaphore. When work completes, signal it.
        const auto pending_signal = std::move(this->pending_signals.front());
        this->pending_signals.pop_front();

        // If we're stopping, signal the semaphore and don't worry about work
        // actually completing.
        if (stoken.stop_requested()) {
            pending_signal.wakeup_semaphore.signal(this->device);
            break;
        }

        // Unlock, wait for work to finish, lock again.
        lock.unlock();
        for (const auto& frame_span : pending_signal.frame_spans) {
            if (frame_span) {
                frame_span->await_completed();
            }
        }
        lock.lock();

        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());
        }
        lock.unlock();

        pending_signal.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 (std::ranges::all_of(this->pending_frame_spans,
                            [](const auto& frame_span) {
                                if (!frame_span) {
                                    return true;
                                }
                                return frame_span->has_completed();
                            })) {
        wakeup_semaphore.signal(this->device);
        this->pending_frame_spans.clear();
        return;
    }

    this->pending_signals.emplace_back(PendingSignal{
        .wakeup_semaphore = wakeup_semaphore,
        .frame_spans = std::move(this->pending_frame_spans),
    });
    this->pending_frame_spans.clear();

    lock.unlock();
    this->cv.notify_one();
}

void SwapchainMonitor::attach_work(
    std::vector<std::unique_ptr<FrameSpan>> frame_spans) {

    const auto lock = std::scoped_lock{this->mutex};
    if (!this->was_low_latency_requested) {
        return;
    }
    this->pending_frame_spans = std::move(frame_spans);
}

} // namespace low_latency