blob: 81934baaa46c6dbf360a8f04e89cb744cc72456a (
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 "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 semaphore_signal =
SemaphoreSignal{info.signalSemaphore, info.value};
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.
semaphore_signal.signal(this->device);
return;
}
iter->second.notify_semaphore(semaphore_signal);
}
} // namespace low_latency
|