1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-02-24 13:54:17 +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
parent cb16701934
commit 5a5f4c587d
3 changed files with 181 additions and 5 deletions

View File

@ -46,6 +46,7 @@ namespace dxvk {
Presenter::~Presenter() { Presenter::~Presenter() {
destroySwapchain(); destroySwapchain();
destroySurface(); destroySurface();
destroyLatencySemaphore();
if (m_frameThread.joinable()) { if (m_frameThread.joinable()) {
{ std::lock_guard<dxvk::mutex> lock(m_frameMutex); { std::lock_guard<dxvk::mutex> 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 // Set dynamic present mode for the next frame if possible
if (!m_dynamicModes.empty()) if (!m_dynamicModes.empty())
m_presentMode = m_dynamicModes.at(m_preferredSyncInterval ? 1u : 0u); m_presentMode = m_dynamicModes.at(m_preferredSyncInterval ? 1u : 0u);
@ -152,13 +163,16 @@ namespace dxvk {
} }
VkResult Presenter::presentImage(uint64_t frameId) { VkResult Presenter::presentImage(uint64_t frameId, uint64_t trackedId) {
PresenterSync& currSync = m_semaphores.at(m_frameIndex); PresenterSync& currSync = m_semaphores.at(m_frameIndex);
VkPresentIdKHR presentId = { VK_STRUCTURE_TYPE_PRESENT_ID_KHR }; VkPresentIdKHR presentId = { VK_STRUCTURE_TYPE_PRESENT_ID_KHR };
presentId.swapchainCount = 1; presentId.swapchainCount = 1;
presentId.pPresentIds = &frameId; presentId.pPresentIds = &frameId;
if (m_latencySleepSupported)
presentId.pPresentIds = &trackedId;
VkSwapchainPresentFenceInfoEXT fenceInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_FENCE_INFO_EXT }; VkSwapchainPresentFenceInfoEXT fenceInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_FENCE_INFO_EXT };
fenceInfo.swapchainCount = 1; fenceInfo.swapchainCount = 1;
fenceInfo.pFences = &currSync.fence; fenceInfo.pFences = &currSync.fence;
@ -218,6 +232,9 @@ namespace dxvk {
m_dirtySwapchain = true; m_dirtySwapchain = true;
} }
if (m_latencySleepSupported)
m_latencyMaxTrackedId = trackedId;
m_presentPending = false; m_presentPending = false;
m_surfaceCond.notify_one(); m_surfaceCond.notify_one();
return status; return status;
@ -241,6 +258,7 @@ namespace dxvk {
frame.trackedId = trackedId; frame.trackedId = trackedId;
frame.mode = m_presentMode; frame.mode = m_presentMode;
frame.result = result; frame.result = result;
frame.ll2Mode = m_latencySleepSupported;
m_frameQueue.push(frame); m_frameQueue.push(frame);
m_frameCond.notify_one(); m_frameCond.notify_one();
@ -303,6 +321,83 @@ 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;
}
void Presenter::setLatencyMarkerNv(
uint64_t trackedId,
VkLatencyMarkerNV marker) {
std::unique_lock lock(m_surfaceMutex);
if (!m_latencySleepSupported)
return;
VkSetLatencyMarkerInfoNV info = { VK_STRUCTURE_TYPE_SET_LATENCY_MARKER_INFO_NV };
info.presentID = trackedId;
info.marker = marker;
m_vkd->vkSetLatencyMarkerNV(m_vkd->device(), m_swapchain, &info);
}
void Presenter::latencySleepNv(
uint64_t trackedId) {
std::unique_lock lock(m_surfaceMutex);
if (!m_latencySleepSupported)
return;
if (trackedId > m_latencyMaxTrackedId) {
Logger::warn(str::format("Presenter: Tracked frame ID ", trackedId,
" greater than last presented ID ", m_latencyMaxTrackedId, ", skipping sleep"));
return;
}
if (!m_latencySemaphore) {
if (createLatencySemaphore() != VK_SUCCESS)
return;
}
VkLatencySleepInfoNV info = { VK_STRUCTURE_TYPE_LATENCY_SLEEP_INFO_NV };
info.signalSemaphore = m_latencySemaphore;
info.value = trackedId;
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 = &trackedId;
m_vkd->vkWaitSemaphores(m_vkd->device(), &waitInfo, ~0ull);
}
void Presenter::setSyncInterval(uint32_t syncInterval) { void Presenter::setSyncInterval(uint32_t syncInterval) {
std::lock_guard lock(m_surfaceMutex); std::lock_guard lock(m_surfaceMutex);
@ -539,6 +634,9 @@ namespace dxvk {
modeInfo.presentModeCount = compatibleModes.size(); modeInfo.presentModeCount = compatibleModes.size();
modeInfo.pPresentModes = compatibleModes.data(); 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 }; VkSwapchainCreateInfoKHR swapInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };
swapInfo.surface = m_surface; swapInfo.surface = m_surface;
swapInfo.minImageCount = pickImageCount(minImageCount, maxImageCount); swapInfo.minImageCount = pickImageCount(minImageCount, maxImageCount);
@ -560,6 +658,9 @@ namespace dxvk {
if (m_device->features().extSwapchainMaintenance1.swapchainMaintenance1) if (m_device->features().extSwapchainMaintenance1.swapchainMaintenance1)
modeInfo.pNext = std::exchange(swapInfo.pNext, &modeInfo); modeInfo.pNext = std::exchange(swapInfo.pNext, &modeInfo);
if (m_device->features().nvLowLatency2)
latencyInfo.pNext = std::exchange(swapInfo.pNext, &latencyInfo);
Logger::info(str::format( Logger::info(str::format(
"Presenter: Actual swapchain properties:" "Presenter: Actual swapchain properties:"
"\n Format: ", swapInfo.imageFormat, "\n Format: ", swapInfo.imageFormat,
@ -637,7 +738,7 @@ namespace dxvk {
} }
// Invalidate indices // Invalidate indices
m_hdrMetadataDirty = true; m_latencySleepSupported = m_device->features().nvLowLatency2 && latencyInfo.latencyModeEnable;
m_imageIndex = 0; m_imageIndex = 0;
m_frameIndex = 0; m_frameIndex = 0;
@ -982,6 +1083,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() { void Presenter::destroySwapchain() {
if (m_signal != nullptr) if (m_signal != nullptr)
m_signal->wait(m_lastFrameId.load(std::memory_order_acquire)); m_signal->wait(m_lastFrameId.load(std::memory_order_acquire));
@ -1005,6 +1120,11 @@ namespace dxvk {
m_acquireStatus = VK_NOT_READY; m_acquireStatus = VK_NOT_READY;
m_presentPending = false; m_presentPending = false;
m_hdrMetadataDirty = true;
m_latencySleepModeDirty = true;
m_latencySleepSupported = false;
} }
@ -1015,6 +1135,13 @@ namespace dxvk {
} }
void Presenter::destroyLatencySemaphore() {
m_vkd->vkDestroySemaphore(m_vkd->device(), m_latencySemaphore, nullptr);
m_latencySemaphore = VK_NULL_HANDLE;
}
void Presenter::waitForSwapchainFence( void Presenter::waitForSwapchainFence(
PresenterSync& sync) { PresenterSync& sync) {
if (!sync.fenceSignaled) if (!sync.fenceSignaled)
@ -1056,8 +1183,10 @@ namespace dxvk {
// Don't bother with it on MAILBOX / IMMEDIATE modes since doing so would // Don't bother with it on MAILBOX / IMMEDIATE modes since doing so would
// restrict us to the display refresh rate on some platforms (XWayland). // restrict us to the display refresh rate on some platforms (XWayland).
if (frame.result >= 0 && (frame.mode == VK_PRESENT_MODE_FIFO_KHR || frame.mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR)) { if (frame.result >= 0 && (frame.mode == VK_PRESENT_MODE_FIFO_KHR || frame.mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR)) {
uint64_t frameId = frame.ll2Mode ? frame.trackedId : frame.frameId;
VkResult vr = m_vkd->vkWaitForPresentKHR(m_vkd->device(), VkResult vr = m_vkd->vkWaitForPresentKHR(m_vkd->device(),
m_swapchain, frame.frameId, std::numeric_limits<uint64_t>::max()); m_swapchain, frameId, std::numeric_limits<uint64_t>::max());
if (vr < 0 && vr != VK_ERROR_OUT_OF_DATE_KHR && vr != VK_ERROR_SURFACE_LOST_KHR) if (vr < 0 && vr != VK_ERROR_OUT_OF_DATE_KHR && vr != VK_ERROR_SURFACE_LOST_KHR)
Logger::err(str::format("Presenter: vkWaitForPresentKHR failed: ", vr)); Logger::err(str::format("Presenter: vkWaitForPresentKHR failed: ", vr));

View File

@ -61,6 +61,7 @@ namespace dxvk {
uint64_t trackedId = 0u; uint64_t trackedId = 0u;
VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR; VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR;
VkResult result = VK_NOT_READY; VkResult result = VK_NOT_READY;
VkBool32 ll2Mode = VK_FALSE;
}; };
/** /**
@ -125,10 +126,13 @@ namespace dxvk {
* *
* Presents the last successfuly acquired image. * Presents the last successfuly acquired image.
* \param [in] frameId Frame number. * \param [in] frameId Frame number.
* \param [in] trackedId Latency frame ID
* Must increase monotonically. * Must increase monotonically.
* \returns Status of the operation * \returns Status of the operation
*/ */
VkResult presentImage(uint64_t frameId); VkResult presentImage(
uint64_t frameId,
uint64_t trackedId);
/** /**
* \brief Signals a given frame * \brief Signals a given frame
@ -220,6 +224,37 @@ namespace dxvk {
*/ */
void destroyResources(); 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] trackedId Tracked frame ID
* \param [in] marker Marker
*/
void setLatencyMarkerNv(
uint64_t trackedId,
VkLatencyMarkerNV marker);
/**
* \brief Executes latency sleep
*
* Ignored if the current swapchain has not been
* created with low latency support.
* \param [in] trackedId Tracked frame ID
*/
void latencySleepNv(
uint64_t trackedId);
private: private:
Rc<DxvkDevice> m_device; Rc<DxvkDevice> m_device;
@ -261,6 +296,13 @@ namespace dxvk {
std::optional<VkHdrMetadataEXT> m_hdrMetadata; std::optional<VkHdrMetadataEXT> m_hdrMetadata;
bool m_hdrMetadataDirty = false; bool m_hdrMetadataDirty = false;
std::optional<VkLatencySleepModeInfoNV> m_latencySleepMode;
VkSemaphore m_latencySemaphore = VK_NULL_HANDLE;
uint64_t m_latencyMaxTrackedId = 0u;
bool m_latencySleepModeDirty = false;
bool m_latencySleepSupported = false;
alignas(CACHE_LINE_SIZE) alignas(CACHE_LINE_SIZE)
dxvk::mutex m_frameMutex; dxvk::mutex m_frameMutex;
dxvk::condition_variable m_frameCond; dxvk::condition_variable m_frameCond;
@ -320,10 +362,14 @@ namespace dxvk {
VkResult createSurface(); VkResult createSurface();
VkResult createLatencySemaphore();
void destroySwapchain(); void destroySwapchain();
void destroySurface(); void destroySurface();
void destroyLatencySemaphore();
void waitForSwapchainFence( void waitForSwapchainFence(
PresenterSync& sync); PresenterSync& sync);

View File

@ -159,7 +159,8 @@ namespace dxvk {
if (entry.latency.tracker) if (entry.latency.tracker)
entry.latency.tracker->notifyQueuePresentBegin(entry.latency.trackedId); entry.latency.tracker->notifyQueuePresentBegin(entry.latency.trackedId);
entry.result = entry.present.presenter->presentImage(entry.present.frameId); entry.result = entry.present.presenter->presentImage(
entry.present.frameId, entry.latency.trackedId);
if (entry.latency.tracker) { if (entry.latency.tracker) {
entry.latency.tracker->notifyQueuePresentEnd( entry.latency.tracker->notifyQueuePresentEnd(