From e603edadf79580d923c6feb92899dec310197b5f Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sun, 19 Jan 2025 02:44:20 +0100 Subject: [PATCH] [dxvk] Implement NV_low_latency2 functionality in presenter --- src/dxvk/dxvk_presenter.cpp | 137 +++++++++++++++++++++++++++++++++++- src/dxvk/dxvk_presenter.h | 56 ++++++++++++++- 2 files changed, 190 insertions(+), 3 deletions(-) diff --git a/src/dxvk/dxvk_presenter.cpp b/src/dxvk/dxvk_presenter.cpp index 63e9b91c7..c9cdc8dad 100644 --- a/src/dxvk/dxvk_presenter.cpp +++ b/src/dxvk/dxvk_presenter.cpp @@ -46,6 +46,7 @@ namespace dxvk { Presenter::~Presenter() { destroySwapchain(); destroySurface(); + destroyLatencySemaphore(); if (m_frameThread.joinable()) { { std::lock_guard lock(m_frameMutex); @@ -139,6 +140,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); @@ -301,6 +312,98 @@ 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"); + + if (m_latencySleepMode) { + m_latencySleepModeDirty |= + m_latencySleepMode->lowLatencyMode != sleepMode.lowLatencyMode || + m_latencySleepMode->lowLatencyBoost != sleepMode.lowLatencyBoost || + m_latencySleepMode->minimumIntervalUs != sleepMode.minimumIntervalUs; + } else { + m_dirtySwapchain = true; + + m_latencySleepModeDirty = true; + } + + 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_latencySleepSupported) + return dxvk::high_resolution_clock::time_point(); + + auto t0 = dxvk::high_resolution_clock::now(); + + 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 t1 + (t1 - t0) / 2u; + } + + + void Presenter::latencySleepNv() { + std::unique_lock lock(m_surfaceMutex); + + if (!m_latencySleepSupported) + return; + + if (!m_latencySemaphore) { + if (createLatencySemaphore() != VK_SUCCESS) + return; + } + + 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(); + + VkSemaphoreWaitInfo waitInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO }; + waitInfo.semaphoreCount = 1; + waitInfo.pSemaphores = &m_latencySemaphore; + waitInfo.pValues = &info.value; + + m_vkd->vkWaitSemaphores(m_vkd->device(), &waitInfo, ~0ull); + } + + + 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); @@ -537,6 +640,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); @@ -558,6 +664,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, @@ -635,7 +744,7 @@ namespace dxvk { } // Invalidate indices - m_hdrMetadataDirty = true; + m_latencySleepSupported = m_device->features().nvLowLatency2 && latencyInfo.latencyModeEnable; m_imageIndex = 0; m_frameIndex = 0; @@ -980,6 +1089,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)); @@ -1003,6 +1126,11 @@ namespace dxvk { m_acquireStatus = VK_NOT_READY; m_presentPending = false; + + m_hdrMetadataDirty = true; + + m_latencySleepModeDirty = true; + m_latencySleepSupported = false; } @@ -1013,6 +1141,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) diff --git a/src/dxvk/dxvk_presenter.h b/src/dxvk/dxvk_presenter.h index 633ca7753..4ff351d5a 100644 --- a/src/dxvk/dxvk_presenter.h +++ b/src/dxvk/dxvk_presenter.h @@ -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,47 @@ 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. + */ + void 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 m_device; @@ -258,6 +299,13 @@ namespace dxvk { std::optional m_hdrMetadata; bool m_hdrMetadataDirty = false; + std::optional 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 +365,14 @@ namespace dxvk { VkResult createSurface(); + VkResult createLatencySemaphore(); + void destroySwapchain(); void destroySurface(); + void destroyLatencySemaphore(); + void waitForSwapchainFence( PresenterSync& sync);