mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-03-15 07:29:17 +01:00
[dxvk] Implement dynamic present mode switching
This way, we don't need to recreate the swap chain when the app switches between vsync enabled and disabled. Currently only works when running bleeding-edge Gamescope with ENABLE_GAMESCOPE_WSI=1.
This commit is contained in:
parent
e6be0cf996
commit
85d52ccb88
@ -1,3 +1,5 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "dxvk_device.h"
|
#include "dxvk_device.h"
|
||||||
#include "dxvk_presenter.h"
|
#include "dxvk_presenter.h"
|
||||||
|
|
||||||
@ -52,6 +54,10 @@ namespace dxvk {
|
|||||||
VkResult Presenter::presentImage(VkPresentModeKHR mode) {
|
VkResult Presenter::presentImage(VkPresentModeKHR mode) {
|
||||||
PresenterSync sync = m_semaphores.at(m_frameIndex);
|
PresenterSync sync = m_semaphores.at(m_frameIndex);
|
||||||
|
|
||||||
|
VkSwapchainPresentModeInfoEXT modeInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT };
|
||||||
|
modeInfo.swapchainCount = 1;
|
||||||
|
modeInfo.pPresentModes = &mode;
|
||||||
|
|
||||||
VkPresentInfoKHR info = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
|
VkPresentInfoKHR info = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
|
||||||
info.waitSemaphoreCount = 1;
|
info.waitSemaphoreCount = 1;
|
||||||
info.pWaitSemaphores = &sync.present;
|
info.pWaitSemaphores = &sync.present;
|
||||||
@ -59,6 +65,9 @@ namespace dxvk {
|
|||||||
info.pSwapchains = &m_swapchain;
|
info.pSwapchains = &m_swapchain;
|
||||||
info.pImageIndices = &m_imageIndex;
|
info.pImageIndices = &m_imageIndex;
|
||||||
|
|
||||||
|
if (m_device->features().extSwapchainMaintenance1.swapchainMaintenance1)
|
||||||
|
info.pNext = &modeInfo;
|
||||||
|
|
||||||
VkResult status = m_vkd->vkQueuePresentKHR(
|
VkResult status = m_vkd->vkQueuePresentKHR(
|
||||||
m_device->queues().graphics.queueHandle, &info);
|
m_device->queues().graphics.queueHandle, &info);
|
||||||
|
|
||||||
@ -103,30 +112,39 @@ namespace dxvk {
|
|||||||
if (!m_surface)
|
if (!m_surface)
|
||||||
return VK_ERROR_SURFACE_LOST_KHR;
|
return VK_ERROR_SURFACE_LOST_KHR;
|
||||||
|
|
||||||
// Query surface capabilities. Some properties might
|
VkSurfaceFullScreenExclusiveInfoEXT fullScreenExclusiveInfo = { VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT };
|
||||||
// have changed, including the size limits and supported
|
fullScreenExclusiveInfo.fullScreenExclusive = desc.fullScreenExclusive;
|
||||||
// present modes, so we'll just query everything again.
|
|
||||||
VkSurfaceCapabilitiesKHR caps;
|
VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR };
|
||||||
|
surfaceInfo.surface = m_surface;
|
||||||
|
|
||||||
|
if (m_device->features().extFullScreenExclusive)
|
||||||
|
surfaceInfo.pNext = &fullScreenExclusiveInfo;
|
||||||
|
|
||||||
|
// Query surface capabilities. Some properties might have changed,
|
||||||
|
// including the size limits and supported present modes, so we'll
|
||||||
|
// just query everything again.
|
||||||
|
VkSurfaceCapabilities2KHR caps = { VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR };
|
||||||
|
|
||||||
std::vector<VkSurfaceFormatKHR> formats;
|
std::vector<VkSurfaceFormatKHR> formats;
|
||||||
std::vector<VkPresentModeKHR> modes;
|
std::vector<VkPresentModeKHR> modes;
|
||||||
|
|
||||||
VkResult status;
|
VkResult status;
|
||||||
|
|
||||||
if ((status = m_vki->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
|
if (m_device->features().extFullScreenExclusive) {
|
||||||
m_device->adapter()->handle(), m_surface, &caps)))
|
status = m_vki->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
|
||||||
|
m_device->adapter()->handle(), m_surface, &caps.surfaceCapabilities);
|
||||||
|
} else {
|
||||||
|
status = m_vki->vkGetPhysicalDeviceSurfaceCapabilities2KHR(
|
||||||
|
m_device->adapter()->handle(), &surfaceInfo, &caps);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
if ((status = getSupportedFormats(formats, desc.fullScreenExclusive)))
|
// Select image extent based on current surface capabilities, and return
|
||||||
return status;
|
// immediately if we cannot create an actual swap chain.
|
||||||
|
m_info.imageExtent = pickImageExtent(caps.surfaceCapabilities, desc.imageExtent);
|
||||||
if ((status = getSupportedPresentModes(modes, desc.fullScreenExclusive)))
|
|
||||||
return status;
|
|
||||||
|
|
||||||
// Select actual swap chain properties and create swap chain
|
|
||||||
m_info.format = pickFormat(formats.size(), formats.data(), desc.numFormats, desc.formats);
|
|
||||||
m_info.presentMode = pickPresentMode(modes.size(), modes.data(), m_info.syncInterval);
|
|
||||||
m_info.imageExtent = pickImageExtent(caps, desc.imageExtent);
|
|
||||||
m_info.imageCount = pickImageCount(caps, m_info.presentMode, desc.imageCount);
|
|
||||||
|
|
||||||
if (!m_info.imageExtent.width || !m_info.imageExtent.height) {
|
if (!m_info.imageExtent.width || !m_info.imageExtent.height) {
|
||||||
m_info.imageCount = 0;
|
m_info.imageCount = 0;
|
||||||
@ -134,9 +152,102 @@ namespace dxvk {
|
|||||||
return VK_SUCCESS;
|
return VK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Select format based on swap chain properties
|
||||||
|
if ((status = getSupportedFormats(formats, desc.fullScreenExclusive)))
|
||||||
|
return status;
|
||||||
|
|
||||||
|
m_info.format = pickFormat(formats.size(), formats.data(), desc.numFormats, desc.formats);
|
||||||
|
|
||||||
|
// Select a present mode for the current sync interval
|
||||||
|
if ((status = getSupportedPresentModes(modes, desc.fullScreenExclusive)))
|
||||||
|
return status;
|
||||||
|
|
||||||
|
m_info.presentMode = pickPresentMode(modes.size(), modes.data(), m_info.syncInterval);
|
||||||
|
|
||||||
|
// Check whether we can change present modes dynamically. This may
|
||||||
|
// influence the image count as well as further swap chain creation.
|
||||||
|
std::vector<VkPresentModeKHR> dynamicModes = {{
|
||||||
|
pickPresentMode(modes.size(), modes.data(), 0),
|
||||||
|
pickPresentMode(modes.size(), modes.data(), 1),
|
||||||
|
}};
|
||||||
|
|
||||||
|
std::vector<VkPresentModeKHR> compatibleModes;
|
||||||
|
|
||||||
|
// As for the minimum image count, start with the most generic value
|
||||||
|
// that works with all present modes.
|
||||||
|
uint32_t minImageCount = caps.surfaceCapabilities.minImageCount;
|
||||||
|
uint32_t maxImageCount = caps.surfaceCapabilities.maxImageCount;
|
||||||
|
|
||||||
|
if (m_device->features().extSwapchainMaintenance1.swapchainMaintenance1) {
|
||||||
|
VkSurfacePresentModeCompatibilityEXT compatibleModeInfo = { VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_EXT };
|
||||||
|
|
||||||
|
VkSurfacePresentModeEXT presentModeInfo = { VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT };
|
||||||
|
presentModeInfo.pNext = const_cast<void*>(std::exchange(surfaceInfo.pNext, &presentModeInfo));
|
||||||
|
presentModeInfo.presentMode = m_info.presentMode;
|
||||||
|
|
||||||
|
caps.pNext = &compatibleModeInfo;
|
||||||
|
|
||||||
|
if ((status = m_vki->vkGetPhysicalDeviceSurfaceCapabilities2KHR(
|
||||||
|
m_device->adapter()->handle(), &surfaceInfo, &caps)))
|
||||||
|
return status;
|
||||||
|
|
||||||
|
compatibleModes.resize(compatibleModeInfo.presentModeCount);
|
||||||
|
compatibleModeInfo.pPresentModes = compatibleModes.data();
|
||||||
|
|
||||||
|
if ((status = m_vki->vkGetPhysicalDeviceSurfaceCapabilities2KHR(
|
||||||
|
m_device->adapter()->handle(), &surfaceInfo, &caps)))
|
||||||
|
return status;
|
||||||
|
|
||||||
|
// Remove modes we don't need for the purpose of finding the minimum
|
||||||
|
// image count, as well as for swap chain creation later.
|
||||||
|
compatibleModes.erase(std::remove_if(compatibleModes.begin(), compatibleModes.end(),
|
||||||
|
[&dynamicModes] (VkPresentModeKHR mode) {
|
||||||
|
return std::find(dynamicModes.begin(), dynamicModes.end(), mode) == dynamicModes.end();
|
||||||
|
}), compatibleModes.end());
|
||||||
|
|
||||||
|
minImageCount = 0;
|
||||||
|
caps.pNext = nullptr;
|
||||||
|
|
||||||
|
for (auto mode : compatibleModes) {
|
||||||
|
presentModeInfo.presentMode = mode;
|
||||||
|
|
||||||
|
if ((status = m_vki->vkGetPhysicalDeviceSurfaceCapabilities2KHR(
|
||||||
|
m_device->adapter()->handle(), &surfaceInfo, &caps)))
|
||||||
|
return status;
|
||||||
|
|
||||||
|
minImageCount = std::max(minImageCount, caps.surfaceCapabilities.minImageCount);
|
||||||
|
|
||||||
|
if (caps.surfaceCapabilities.maxImageCount) {
|
||||||
|
maxImageCount = maxImageCount
|
||||||
|
? std::min(maxImageCount, caps.surfaceCapabilities.maxImageCount)
|
||||||
|
: caps.surfaceCapabilities.maxImageCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If any required mode is not supported for dynamic present
|
||||||
|
// mode switching, clear the dynamic mode array.
|
||||||
|
for (auto mode : dynamicModes) {
|
||||||
|
if (std::find(compatibleModes.begin(), compatibleModes.end(), mode) == compatibleModes.end()) {
|
||||||
|
dynamicModes.clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (dynamicModes[0] != dynamicModes[1]) {
|
||||||
|
// If we can't switch modes dynamically, clear the
|
||||||
|
// array so that setSyncInterval errors out properly.
|
||||||
|
dynamicModes.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute swap chain image count based on available info
|
||||||
|
m_info.imageCount = pickImageCount(minImageCount, maxImageCount, desc.imageCount);
|
||||||
|
|
||||||
VkSurfaceFullScreenExclusiveInfoEXT fullScreenInfo = { VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT };
|
VkSurfaceFullScreenExclusiveInfoEXT fullScreenInfo = { VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT };
|
||||||
fullScreenInfo.fullScreenExclusive = desc.fullScreenExclusive;
|
fullScreenInfo.fullScreenExclusive = desc.fullScreenExclusive;
|
||||||
|
|
||||||
|
VkSwapchainPresentModesCreateInfoEXT modeInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODES_CREATE_INFO_EXT };
|
||||||
|
modeInfo.presentModeCount = compatibleModes.size();
|
||||||
|
modeInfo.pPresentModes = compatibleModes.data();
|
||||||
|
|
||||||
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 = m_info.imageCount;
|
swapInfo.minImageCount = m_info.imageCount;
|
||||||
@ -151,16 +262,18 @@ namespace dxvk {
|
|||||||
swapInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
swapInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||||
swapInfo.presentMode = m_info.presentMode;
|
swapInfo.presentMode = m_info.presentMode;
|
||||||
swapInfo.clipped = VK_TRUE;
|
swapInfo.clipped = VK_TRUE;
|
||||||
swapInfo.oldSwapchain = VK_NULL_HANDLE;
|
|
||||||
|
|
||||||
if (m_device->features().extFullScreenExclusive)
|
if (m_device->features().extFullScreenExclusive)
|
||||||
swapInfo.pNext = &fullScreenInfo;
|
fullScreenInfo.pNext = const_cast<void*>(std::exchange(swapInfo.pNext, &fullScreenInfo));
|
||||||
|
|
||||||
|
if (m_device->features().extSwapchainMaintenance1.swapchainMaintenance1)
|
||||||
|
modeInfo.pNext = std::exchange(swapInfo.pNext, &modeInfo);
|
||||||
|
|
||||||
Logger::info(str::format(
|
Logger::info(str::format(
|
||||||
"Presenter: Actual swap chain properties:"
|
"Presenter: Actual swap chain properties:"
|
||||||
"\n Format: ", m_info.format.format,
|
"\n Format: ", m_info.format.format,
|
||||||
"\n Color space: ", m_info.format.colorSpace,
|
"\n Color space: ", m_info.format.colorSpace,
|
||||||
"\n Present mode: ", m_info.presentMode,
|
"\n Present mode: ", m_info.presentMode, " (dynamic: ", (dynamicModes.empty() ? "no)" : "yes)"),
|
||||||
"\n Buffer size: ", m_info.imageExtent.width, "x", m_info.imageExtent.height,
|
"\n Buffer size: ", m_info.imageExtent.width, "x", m_info.imageExtent.height,
|
||||||
"\n Image count: ", m_info.imageCount,
|
"\n Image count: ", m_info.imageCount,
|
||||||
"\n Exclusive FS: ", desc.fullScreenExclusive));
|
"\n Exclusive FS: ", desc.fullScreenExclusive));
|
||||||
@ -217,6 +330,8 @@ namespace dxvk {
|
|||||||
m_imageIndex = 0;
|
m_imageIndex = 0;
|
||||||
m_frameIndex = 0;
|
m_frameIndex = 0;
|
||||||
m_acquireStatus = VK_NOT_READY;
|
m_acquireStatus = VK_NOT_READY;
|
||||||
|
|
||||||
|
m_dynamicModes = std::move(dynamicModes);
|
||||||
return VK_SUCCESS;
|
return VK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,11 +350,20 @@ namespace dxvk {
|
|||||||
|
|
||||||
|
|
||||||
VkResult Presenter::setSyncInterval(uint32_t syncInterval) {
|
VkResult Presenter::setSyncInterval(uint32_t syncInterval) {
|
||||||
|
// Normalize sync interval for present modes. We currently
|
||||||
|
// cannot support anything other than 1 natively anyway.
|
||||||
|
syncInterval = std::min(syncInterval, 1u);
|
||||||
|
|
||||||
if (syncInterval == m_info.syncInterval)
|
if (syncInterval == m_info.syncInterval)
|
||||||
return VK_SUCCESS;
|
return VK_SUCCESS;
|
||||||
|
|
||||||
m_info.syncInterval = syncInterval;
|
m_info.syncInterval = syncInterval;
|
||||||
|
|
||||||
|
if (syncInterval >= m_dynamicModes.size())
|
||||||
return VK_ERROR_OUT_OF_DATE_KHR;
|
return VK_ERROR_OUT_OF_DATE_KHR;
|
||||||
|
|
||||||
|
m_info.presentMode = m_dynamicModes[syncInterval];
|
||||||
|
return VK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -426,19 +550,16 @@ namespace dxvk {
|
|||||||
|
|
||||||
|
|
||||||
uint32_t Presenter::pickImageCount(
|
uint32_t Presenter::pickImageCount(
|
||||||
const VkSurfaceCapabilitiesKHR& caps,
|
uint32_t minImageCount,
|
||||||
VkPresentModeKHR presentMode,
|
uint32_t maxImageCount,
|
||||||
uint32_t desired) {
|
uint32_t desired) {
|
||||||
uint32_t count = caps.minImageCount;
|
uint32_t count = minImageCount + 1;
|
||||||
|
|
||||||
if (presentMode != VK_PRESENT_MODE_IMMEDIATE_KHR)
|
|
||||||
count = caps.minImageCount + 1;
|
|
||||||
|
|
||||||
if (count < desired)
|
if (count < desired)
|
||||||
count = desired;
|
count = desired;
|
||||||
|
|
||||||
if (count > caps.maxImageCount && caps.maxImageCount != 0)
|
if (count > maxImageCount && maxImageCount != 0)
|
||||||
count = caps.maxImageCount;
|
count = maxImageCount;
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
@ -457,6 +578,7 @@ namespace dxvk {
|
|||||||
|
|
||||||
m_images.clear();
|
m_images.clear();
|
||||||
m_semaphores.clear();
|
m_semaphores.clear();
|
||||||
|
m_dynamicModes.clear();
|
||||||
|
|
||||||
m_swapchain = VK_NULL_HANDLE;
|
m_swapchain = VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
|
@ -208,6 +208,8 @@ namespace dxvk {
|
|||||||
std::vector<PresenterImage> m_images;
|
std::vector<PresenterImage> m_images;
|
||||||
std::vector<PresenterSync> m_semaphores;
|
std::vector<PresenterSync> m_semaphores;
|
||||||
|
|
||||||
|
std::vector<VkPresentModeKHR> m_dynamicModes;
|
||||||
|
|
||||||
uint32_t m_imageIndex = 0;
|
uint32_t m_imageIndex = 0;
|
||||||
uint32_t m_frameIndex = 0;
|
uint32_t m_frameIndex = 0;
|
||||||
|
|
||||||
@ -245,8 +247,8 @@ namespace dxvk {
|
|||||||
VkExtent2D desired);
|
VkExtent2D desired);
|
||||||
|
|
||||||
uint32_t pickImageCount(
|
uint32_t pickImageCount(
|
||||||
const VkSurfaceCapabilitiesKHR& caps,
|
uint32_t minImageCount,
|
||||||
VkPresentModeKHR presentMode,
|
uint32_t maxImageCount,
|
||||||
uint32_t desired);
|
uint32_t desired);
|
||||||
|
|
||||||
VkResult createSurface();
|
VkResult createSurface();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user