aboutsummaryrefslogtreecommitdiff
path: root/src/swapchain_monitor.cc
blob: adeb3157f1ae3969df20215432ec7f134d53376c (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
#include "swapchain_monitor.hh"
#include "device_context.hh"
#include "helper.hh"

#include <vulkan/vulkan_core.h>

#include <functional>
#include <mutex>

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)) {}

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::do_swapchain_monitor(const std::stop_token stoken) {
    for (;;) {
        auto lock = std::unique_lock{this->mutex};
        this->cv.wait(lock, stoken,
                      [&]() { return !this->wakeup_semaphores.empty(); });

        if (stoken.stop_requested()) {
            // Small chance an application might need outstanding semaphores
            // to be signalled if it's closing to avoid a hang.
            break;
        }

        // Look for the latest submission and make sure it's completed.
        if (!this->in_flight_submissions.empty()) {
            const auto submission = this->in_flight_submissions.back();
            this->in_flight_submissions.clear();

            if (!submission->empty()) {
                submission->back()->tail_handle->await_time();
            }
        }

        // We might want to signal them all? In theory it's the same timeline
        // semaphore so obviously it's redundant to signal them one by one. In
        // almost all cases, there should just be one here anyway.
        const auto wakeup_semaphore = this->wakeup_semaphores.back();
        wakeup_semaphores.clear();

        wakeup_semaphore.signal(this->device);
    }
}

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) {

    const auto lock = std::scoped_lock{this->mutex};

    const auto wakeup_semaphore = WakeupSemaphore{
        .timeline_semaphore = timeline_semaphore, .value = value};

    // Signal immediately if low_latency isn't requested or if we have no
    // outstanding work.
    if (!this->was_low_latency_requested ||
        this->in_flight_submissions.empty()) {

        wakeup_semaphore.signal(this->device);
        return;
    }

    this->wakeup_semaphores.emplace_back(timeline_semaphore, value);
    this->cv.notify_one();
}

void SwapchainMonitor::notify_present(
    const QueueContext::submissions_t& submissions) {

    const auto lock = std::scoped_lock{this->mutex};

    // Fast path where this work has already completed.
    if (!this->wakeup_semaphores.empty() && !submissions->empty()) {

        const auto& finished = submissions->back()->tail_handle->get_time();
        if (finished.has_value()) {
            this->wakeup_semaphores.back().signal(this->device);
            this->wakeup_semaphores.clear();
            return;
        }
    }

    this->in_flight_submissions.emplace_back(submissions);
    this->cv.notify_one();
}

void SwapchainMonitor::wait_until() {
    // No reason to lock when using VK_AMD_anti_lag.
    if (this->in_flight_submissions.empty()) {
        return;
    }

    const auto last_submissions = this->in_flight_submissions.back();
    this->in_flight_submissions.clear();
    if (last_submissions->empty()) {
        return;
    }

    last_submissions->back()->tail_handle->await_time();
}

} // namespace low_latency