aboutsummaryrefslogtreecommitdiff
path: root/src/strategies/low_latency2/device_strategy.cc
blob: f82098d18900d3e0891a9df61b7a68030fba7e73 (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
130
#include "device_strategy.hh"
#include "device_context.hh"
#include "queue_strategy.hh"

#include "helper.hh"
#include <mutex>
#include <vulkan/vulkan_core.h>

namespace low_latency {

LowLatency2DeviceStrategy::LowLatency2DeviceStrategy(DeviceContext& device)
    : DeviceStrategy(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.
    // Default to enabled - if the app is using VK_NV_low_latency2 at all it
    // wants pacing. VkSwapchainLatencyCreateInfoNV can override this, but
    // apps like CS2 recreate swapchains without it (apparent app bug).
    auto was_low_latency_requested = bool{true};
    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);
    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);
}

void LowLatency2DeviceStrategy::notify_latency_sleep_mode(
    const VkSwapchainKHR& swapchain,
    const VkLatencySleepModeInfoNV* const info) {

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

    const auto iter = this->swapchain_monitors.find(swapchain);
    if (iter == std::end(this->swapchain_monitors)) {
        return;
    }

    using namespace std::chrono;
    if (info) {
        iter->second.update_params(info->lowLatencyMode,
                                   microseconds{info->minimumIntervalUs});
    } else {
        iter->second.update_params(false, 0us);
    }
}

void LowLatency2DeviceStrategy::submit_swapchain_present_id(
    const VkSwapchainKHR& swapchain, const std::uint64_t& present_id) {

    // Iterate through all queues and grab any work that's associated with this
    // present_id. Turn it into a vector of work that we give to our swapchain
    // monitor.
    auto work = [&]() -> std::vector<std::unique_ptr<FrameSpan>> {
        auto work = std::vector<std::unique_ptr<FrameSpan>>{};
        const auto lock = std::shared_lock{this->device.mutex};
        for (const auto& queue_iter : this->device.queues) {
            const auto& queue = queue_iter.second;

            const auto strategy =
                dynamic_cast<LowLatency2QueueStrategy*>(queue->strategy.get());
            assert(strategy);

            if (strategy->is_out_of_band.load(std::memory_order::relaxed)) {
                continue;
            }

            // Need the lock now - we're modifying it.
            const auto strategy_lock = std::unique_lock{strategy->mutex};
            const auto iter = strategy->frame_spans.find(present_id);
            if (iter == std::end(strategy->frame_spans)) {
                continue;
            }

            // Make sure we clean it up from the present as well.
            work.push_back(std::move(iter->second));
            strategy->frame_spans.erase(iter);
        }
        return work;
    }();

    const auto lock = std::scoped_lock{this->mutex};
    const auto iter = this->swapchain_monitors.find(swapchain);
    if (iter == std::end(this->swapchain_monitors)) {
        return;
    }
    // Notify our monitor that this work has to be completed before they signal
    // whatever semaphore is currently sitting in it.
    iter->second.attach_work(std::move(work));
}

void LowLatency2DeviceStrategy::notify_latency_sleep_nv(
    const VkSwapchainKHR& swapchain, const VkLatencySleepInfoNV& info) {

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

    const auto iter = this->swapchain_monitors.find(swapchain);
    if (iter == std::end(this->swapchain_monitors)) {
        // If we can't find the swapchain we have to signal the semaphore
        // anyway. We must *never* discard these semaphores without signalling
        // them first.
        const auto ssi = VkSemaphoreSignalInfo{
            .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO,
            .semaphore = info.signalSemaphore,
            .value = info.value};
        THROW_NOT_VKSUCCESS(device.vtable.SignalSemaphore(device.device, &ssi));
        return;
    }
    iter->second.notify_semaphore(info.signalSemaphore, info.value);
}

} // namespace low_latency