1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-03-13 19:29:14 +01:00

[dxvk] Implement NV_low_latency2 functionality in presenter

This commit is contained in:
Philip Rebohle 2025-01-19 02:44:20 +01:00 committed by Philip Rebohle
parent c700d76477
commit 4c2990f199
2 changed files with 213 additions and 3 deletions

View File

@ -50,6 +50,7 @@ namespace dxvk {
Presenter::~Presenter() {
destroySwapchain();
destroySurface();
destroyLatencySemaphore();
if (m_frameThread.joinable()) {
{ std::lock_guard<dxvk::mutex> lock(m_frameMutex);
@ -143,6 +144,16 @@ namespace dxvk {
}
}
// Apply latency sleep mode if the swapchain supports it
if (m_latencySleepModeDirty && m_latencySleepMode) {
m_latencySleepModeDirty = false;
if (m_latencySleepSupported) {
m_vkd->vkSetLatencySleepModeNV(m_vkd->device(),
m_swapchain, &(*m_latencySleepMode));
}
}
// Set dynamic present mode for the next frame if possible
if (!m_dynamicModes.empty())
m_presentMode = m_dynamicModes.at(m_preferredSyncInterval ? 1u : 0u);
@ -305,6 +316,120 @@ namespace dxvk {
}
void Presenter::setLatencySleepModeNv(
const VkLatencySleepModeInfoNV& sleepMode) {
std::unique_lock lock(m_surfaceMutex);
if (sleepMode.sType != VK_STRUCTURE_TYPE_LATENCY_SLEEP_MODE_INFO_NV)
return;
if (sleepMode.pNext)
Logger::warn("Presenter: Extended sleep mode info not supported");
// Avoid creating a swapchain with low-latency features
// enabled if the functionality isn't required
bool isDefault = !sleepMode.lowLatencyMode
&& !sleepMode.lowLatencyBoost
&& !sleepMode.minimumIntervalUs;
if (!m_latencySleepMode && isDefault)
return;
m_dirtySwapchain |= !m_latencySleepMode;
if (m_latencySleepMode) {
m_latencySleepModeDirty |=
m_latencySleepMode->lowLatencyMode != sleepMode.lowLatencyMode ||
m_latencySleepMode->lowLatencyBoost != sleepMode.lowLatencyBoost ||
m_latencySleepMode->minimumIntervalUs != sleepMode.minimumIntervalUs;
}
m_latencySleepMode = sleepMode;
m_latencySleepMode->pNext = nullptr;
}
dxvk::high_resolution_clock::time_point Presenter::setLatencyMarkerNv(
uint64_t frameId,
VkLatencyMarkerNV marker) {
std::unique_lock lock(m_surfaceMutex);
if (!m_latencySleepMode) {
// Applications may use latency markers without enabling
// low-latency mode, make sure we have a compatible swapchain
m_latencySleepMode = { VK_STRUCTURE_TYPE_LATENCY_SLEEP_MODE_INFO_NV };
m_dirtySwapchain = true;
return dxvk::high_resolution_clock::now();
}
// Return a CPU timestamp to correlate timestamps from
// latency frame reports with actual CPU timestamps
auto t0 = dxvk::high_resolution_clock::now();
if (m_latencySleepSupported) {
VkSetLatencyMarkerInfoNV info = { VK_STRUCTURE_TYPE_SET_LATENCY_MARKER_INFO_NV };
info.presentID = frameId;
info.marker = marker;
m_vkd->vkSetLatencyMarkerNV(m_vkd->device(), m_swapchain, &info);
}
auto t1 = dxvk::high_resolution_clock::now();
return t0 + (t1 - t0) / 2u;
}
dxvk::high_resolution_clock::duration Presenter::latencySleepNv() {
std::unique_lock lock(m_surfaceMutex);
if (!m_latencySleepSupported)
return dxvk::high_resolution_clock::duration(0u);
if (!m_latencySemaphore) {
if (createLatencySemaphore() != VK_SUCCESS)
return dxvk::high_resolution_clock::duration(0u);
}
VkLatencySleepInfoNV info = { VK_STRUCTURE_TYPE_LATENCY_SLEEP_INFO_NV };
info.signalSemaphore = m_latencySemaphore;
info.value = ++m_latencySleepCounter;
m_vkd->vkLatencySleepNV(m_vkd->device(), m_swapchain, &info);
lock.unlock();
auto t0 = dxvk::high_resolution_clock::now();
VkSemaphoreWaitInfo waitInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO };
waitInfo.semaphoreCount = 1;
waitInfo.pSemaphores = &info.signalSemaphore;
waitInfo.pValues = &info.value;
m_vkd->vkWaitSemaphores(m_vkd->device(), &waitInfo, ~0ull);
auto t1 = dxvk::high_resolution_clock::now();
return t1 - t0;
}
uint32_t Presenter::getLatencyTimingsNv(
uint32_t timingCount,
VkLatencyTimingsFrameReportNV* timings) {
std::unique_lock lock(m_surfaceMutex);
if (!m_latencySleepSupported)
return 0u;
VkGetLatencyMarkerInfoNV info = { VK_STRUCTURE_TYPE_GET_LATENCY_MARKER_INFO_NV };
info.timingCount = timingCount;
info.pTimings = timings;
m_vkd->vkGetLatencyTimingsNV(m_vkd->device(), m_swapchain, &info);
return info.timingCount;
}
void Presenter::setSyncInterval(uint32_t syncInterval) {
std::lock_guard lock(m_surfaceMutex);
@ -541,6 +666,9 @@ namespace dxvk {
modeInfo.presentModeCount = compatibleModes.size();
modeInfo.pPresentModes = compatibleModes.data();
VkSwapchainLatencyCreateInfoNV latencyInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_LATENCY_CREATE_INFO_NV };
latencyInfo.latencyModeEnable = m_latencySleepMode.has_value();
VkSwapchainCreateInfoKHR swapInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };
swapInfo.surface = m_surface;
swapInfo.minImageCount = pickImageCount(minImageCount, maxImageCount);
@ -562,6 +690,9 @@ namespace dxvk {
if (m_device->features().extSwapchainMaintenance1.swapchainMaintenance1)
modeInfo.pNext = std::exchange(swapInfo.pNext, &modeInfo);
if (m_device->features().nvLowLatency2)
latencyInfo.pNext = std::exchange(swapInfo.pNext, &latencyInfo);
Logger::info(str::format(
"Presenter: Actual swapchain properties:"
"\n Format: ", swapInfo.imageFormat,
@ -639,7 +770,7 @@ namespace dxvk {
}
// Invalidate indices
m_hdrMetadataDirty = true;
m_latencySleepSupported = m_device->features().nvLowLatency2 && latencyInfo.latencyModeEnable;
m_imageIndex = 0;
m_frameIndex = 0;
@ -984,6 +1115,20 @@ namespace dxvk {
}
VkResult Presenter::createLatencySemaphore() {
VkSemaphoreTypeCreateInfo typeInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO };
typeInfo.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE;
VkSemaphoreCreateInfo info = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, &typeInfo };
VkResult vr = m_vkd->vkCreateSemaphore(m_vkd->device(), &info, nullptr, &m_latencySemaphore);
if (vr != VK_SUCCESS)
Logger::err(str::format("Presenter: Failed to create latency semaphore: ", vr));
return vr;
}
void Presenter::destroySwapchain() {
if (m_signal != nullptr)
m_signal->wait(m_lastFrameId.load(std::memory_order_acquire));
@ -1007,6 +1152,11 @@ namespace dxvk {
m_acquireStatus = VK_NOT_READY;
m_presentPending = false;
m_hdrMetadataDirty = true;
m_latencySleepModeDirty = true;
m_latencySleepSupported = false;
}
@ -1017,6 +1167,13 @@ namespace dxvk {
}
void Presenter::destroyLatencySemaphore() {
m_vkd->vkDestroySemaphore(m_vkd->device(), m_latencySemaphore, nullptr);
m_latencySemaphore = VK_NULL_HANDLE;
}
void Presenter::waitForSwapchainFence(
PresenterSync& sync) {
if (!sync.fenceSignaled)

View File

@ -124,10 +124,10 @@ namespace dxvk {
*
* Presents the last successfuly acquired image.
* \param [in] frameId Frame number.
* Must increase monotonically.
* \returns Status of the operation
*/
VkResult presentImage(uint64_t frameId);
VkResult presentImage(
uint64_t frameId);
/**
* \brief Signals a given frame
@ -217,6 +217,48 @@ namespace dxvk {
*/
void destroyResources();
/**
* \brief Sets latency sleep mode
*
* Any changes will be applied on the next acquire operation.
* \param [in] sleepMode Latency mode info
*/
void setLatencySleepModeNv(
const VkLatencySleepModeInfoNV& sleepMode);
/**
* \brief Sets latency marker
*
* Ignored if the current swapchain has not been
* created with low latency support.
* \param [in] frameId Frame ID
* \param [in] marker Marker
* \returns CPU timestamp of the marker
*/
dxvk::high_resolution_clock::time_point setLatencyMarkerNv(
uint64_t frameId,
VkLatencyMarkerNV marker);
/**
* \brief Executes latency sleep
*
* Ignored if the current swapchain has not been
* created with low latency support.
* \returns Sleep duration
*/
dxvk::high_resolution_clock::duration latencySleepNv();
/**
* \brief Queries latency timings
*
* \param [in] timingCount Number of timings to query
* \param [out] timings Latency timings
* \returns Number of frame reports returned
*/
uint32_t getLatencyTimingsNv(
uint32_t timingCount,
VkLatencyTimingsFrameReportNV* timings);
private:
Rc<DxvkDevice> m_device;
@ -258,6 +300,13 @@ namespace dxvk {
std::optional<VkHdrMetadataEXT> m_hdrMetadata;
bool m_hdrMetadataDirty = false;
std::optional<VkLatencySleepModeInfoNV> m_latencySleepMode;
VkSemaphore m_latencySemaphore = VK_NULL_HANDLE;
uint64_t m_latencySleepCounter = 0u;
bool m_latencySleepModeDirty = false;
bool m_latencySleepSupported = false;
alignas(CACHE_LINE_SIZE)
dxvk::mutex m_frameMutex;
dxvk::condition_variable m_frameCond;
@ -317,10 +366,14 @@ namespace dxvk {
VkResult createSurface();
VkResult createLatencySemaphore();
void destroySwapchain();
void destroySurface();
void destroyLatencySemaphore();
void waitForSwapchainFence(
PresenterSync& sync);