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:
parent
cb16701934
commit
5a5f4c587d
@ -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));
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user