[dxvk] Rework present mode selection for swap chains

This commit is contained in:
Philip Rebohle 2023-05-29 14:51:40 +02:00
parent 1728d9e89d
commit e6be0cf996
10 changed files with 70 additions and 99 deletions

View File

@ -72,7 +72,7 @@ namespace dxvk {
CreateHud();
if (!pDevice->GetOptions()->deferSurfaceCreation)
RecreateSwapChain(false);
RecreateSwapChain();
}
@ -260,17 +260,13 @@ namespace dxvk {
if (options->syncInterval >= 0)
SyncInterval = options->syncInterval;
if (!(PresentFlags & DXGI_PRESENT_TEST)) {
bool vsync = SyncInterval != 0;
m_dirty |= vsync != m_vsync;
m_vsync = vsync;
}
if (!(PresentFlags & DXGI_PRESENT_TEST))
m_dirty |= m_presenter->setSyncInterval(SyncInterval) != VK_SUCCESS;
HRESULT hr = S_OK;
if (!m_presenter->hasSwapChain()) {
RecreateSwapChain(m_vsync);
RecreateSwapChain();
m_dirty = false;
}
@ -284,7 +280,7 @@ namespace dxvk {
return hr;
if (std::exchange(m_dirty, false))
RecreateSwapChain(m_vsync);
RecreateSwapChain();
try {
PresentImage(SyncInterval);
@ -358,7 +354,7 @@ namespace dxvk {
VkResult status = m_presenter->acquireNextImage(sync, imageIndex);
while (status != VK_SUCCESS && status != VK_SUBOPTIMAL_KHR) {
RecreateSwapChain(m_vsync);
RecreateSwapChain();
if (!m_presenter->hasSwapChain())
return DXGI_STATUS_OCCLUDED;
@ -409,6 +405,7 @@ namespace dxvk {
cFrameId = FrameId,
cSync = Sync,
cHud = m_hud,
cPresentMode = m_presenter->info().presentMode,
cCommandList = m_context->endRecording()
] (DxvkContext* ctx) {
cCommandList->setWsiSemaphores(cSync);
@ -417,7 +414,7 @@ namespace dxvk {
if (cHud != nullptr && !cFrameId)
cHud->update();
m_device->presentImage(m_presenter, &m_presentStatus);
m_device->presentImage(m_presenter, cPresentMode, &m_presentStatus);
});
pContext->FlushCsChunk();
@ -429,11 +426,11 @@ namespace dxvk {
VkResult status = m_device->waitForSubmission(&m_presentStatus);
if (status != VK_SUCCESS)
RecreateSwapChain(m_vsync);
RecreateSwapChain();
}
void D3D11SwapChain::RecreateSwapChain(BOOL Vsync) {
void D3D11SwapChain::RecreateSwapChain() {
// Ensure that we can safely destroy the swap chain
m_device->waitForSubmission(&m_presentStatus);
m_device->waitForIdle();
@ -445,7 +442,6 @@ namespace dxvk {
presenterDesc.imageExtent = { m_desc.Width, m_desc.Height };
presenterDesc.imageCount = PickImageCount(m_desc.BufferCount + 1);
presenterDesc.numFormats = PickFormats(m_desc.Format, presenterDesc.formats);
presenterDesc.numPresentModes = PickPresentModes(Vsync, presenterDesc.presentModes);
presenterDesc.fullScreenExclusive = PickFullscreenMode();
VkResult vr = m_presenter->recreateSwapChain(presenterDesc);
@ -481,7 +477,6 @@ namespace dxvk {
presenterDesc.imageExtent = { m_desc.Width, m_desc.Height };
presenterDesc.imageCount = PickImageCount(m_desc.BufferCount + 1);
presenterDesc.numFormats = PickFormats(m_desc.Format, presenterDesc.formats);
presenterDesc.numPresentModes = PickPresentModes(false, presenterDesc.presentModes);
presenterDesc.fullScreenExclusive = PickFullscreenMode();
m_presenter = new Presenter(m_device, presenterDesc);
@ -702,25 +697,6 @@ namespace dxvk {
}
uint32_t D3D11SwapChain::PickPresentModes(
BOOL Vsync,
VkPresentModeKHR* pDstModes) {
uint32_t n = 0;
if (Vsync) {
if (m_parent->GetOptions()->tearFree == Tristate::False)
pDstModes[n++] = VK_PRESENT_MODE_FIFO_RELAXED_KHR;
pDstModes[n++] = VK_PRESENT_MODE_FIFO_KHR;
} else {
if (m_parent->GetOptions()->tearFree != Tristate::True)
pDstModes[n++] = VK_PRESENT_MODE_IMMEDIATE_KHR;
pDstModes[n++] = VK_PRESENT_MODE_MAILBOX_KHR;
}
return n;
}
uint32_t D3D11SwapChain::PickImageCount(
UINT Preferred) {
int32_t option = m_parent->GetOptions()->numBackBuffers;

View File

@ -119,7 +119,6 @@ namespace dxvk {
HANDLE m_processHandle = nullptr;
bool m_dirty = true;
bool m_vsync = true;
VkColorSpaceKHR m_colorspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
@ -135,8 +134,7 @@ namespace dxvk {
void SynchronizePresent();
void RecreateSwapChain(
BOOL Vsync);
void RecreateSwapChain();
void CreateFrameLatencyEvent();
@ -162,10 +160,6 @@ namespace dxvk {
DXGI_FORMAT Format,
VkSurfaceFormatKHR* pDstFormats);
uint32_t PickPresentModes(
BOOL Vsync,
VkPresentModeKHR* pDstModes);
uint32_t PickImageCount(
UINT Preferred);

View File

@ -41,7 +41,7 @@ namespace dxvk {
CreatePresenter();
if (!pDevice->GetOptions()->deferSurfaceCreation)
RecreateSwapChain(false);
RecreateSwapChain();
}
if (FAILED(CreateBackBuffers(m_presentParams.BackBufferCount)))
@ -135,8 +135,6 @@ namespace dxvk {
if (options->presentInterval >= 0)
presentInterval = options->presentInterval;
bool vsync = presentInterval != 0;
HWND window = m_presentParams.hDeviceWindow;
if (hDestWindowOverride != nullptr)
window = hDestWindowOverride;
@ -148,13 +146,13 @@ namespace dxvk {
m_window = window;
m_dirty |= vsync != m_vsync;
if (m_presenter != nullptr) {
m_dirty |= m_presenter->setSyncInterval(presentInterval) != VK_SUCCESS;
m_dirty |= !m_presenter->hasSwapChain();
}
m_dirty |= UpdatePresentRegion(pSourceRect, pDestRect);
m_dirty |= recreate;
m_dirty |= m_presenter != nullptr &&
!m_presenter->hasSwapChain();
m_vsync = vsync;
m_lastDialog = m_dialog;
@ -169,7 +167,7 @@ namespace dxvk {
CreatePresenter();
if (std::exchange(m_dirty, false))
RecreateSwapChain(vsync);
RecreateSwapChain();
// We aren't going to device loss simply because
// 99% of D3D9 games don't handle this properly and
@ -762,7 +760,7 @@ namespace dxvk {
VkResult status = m_presenter->acquireNextImage(sync, imageIndex);
while (status != VK_SUCCESS && status != VK_SUBOPTIMAL_KHR) {
RecreateSwapChain(m_vsync);
RecreateSwapChain();
info = m_presenter->info();
status = m_presenter->acquireNextImage(sync, imageIndex);
@ -817,6 +815,7 @@ namespace dxvk {
cFrameId = FrameId,
cSync = Sync,
cHud = m_hud,
cPresentMode = m_presenter->info().presentMode,
cCommandList = m_context->endRecording()
] (DxvkContext* ctx) {
cCommandList->setWsiSemaphores(cSync);
@ -825,7 +824,7 @@ namespace dxvk {
if (cHud != nullptr && !cFrameId)
cHud->update();
m_device->presentImage(m_presenter, &m_presentStatus);
m_device->presentImage(m_presenter, cPresentMode, &m_presentStatus);
});
m_parent->FlushCsChunk();
@ -837,10 +836,10 @@ namespace dxvk {
VkResult status = m_device->waitForSubmission(&m_presentStatus);
if (status != VK_SUCCESS)
RecreateSwapChain(m_vsync);
RecreateSwapChain();
}
void D3D9SwapChainEx::RecreateSwapChain(BOOL Vsync) {
void D3D9SwapChainEx::RecreateSwapChain() {
// Ensure that we can safely destroy the swap chain
m_device->waitForSubmission(&m_presentStatus);
m_device->waitForIdle();
@ -851,7 +850,6 @@ namespace dxvk {
presenterDesc.imageExtent = GetPresentExtent();
presenterDesc.imageCount = PickImageCount(m_presentParams.BackBufferCount + 1);
presenterDesc.numFormats = PickFormats(EnumerateFormat(m_presentParams.BackBufferFormat), presenterDesc.formats);
presenterDesc.numPresentModes = PickPresentModes(Vsync, presenterDesc.presentModes);
presenterDesc.fullScreenExclusive = PickFullscreenMode();
VkResult vr = m_presenter->recreateSwapChain(presenterDesc);
@ -885,7 +883,6 @@ namespace dxvk {
presenterDesc.imageExtent = GetPresentExtent();
presenterDesc.imageCount = PickImageCount(m_presentParams.BackBufferCount + 1);
presenterDesc.numFormats = PickFormats(EnumerateFormat(m_presentParams.BackBufferFormat), presenterDesc.formats);
presenterDesc.numPresentModes = PickPresentModes(false, presenterDesc.presentModes);
presenterDesc.fullScreenExclusive = PickFullscreenMode();
m_presenter = new Presenter(m_device, presenterDesc);
@ -1121,25 +1118,6 @@ namespace dxvk {
}
uint32_t D3D9SwapChainEx::PickPresentModes(
BOOL Vsync,
VkPresentModeKHR* pDstModes) {
uint32_t n = 0;
if (Vsync) {
if (m_parent->GetOptions()->tearFree == Tristate::False)
pDstModes[n++] = VK_PRESENT_MODE_FIFO_RELAXED_KHR;
pDstModes[n++] = VK_PRESENT_MODE_FIFO_KHR;
} else {
if (m_parent->GetOptions()->tearFree != Tristate::True)
pDstModes[n++] = VK_PRESENT_MODE_IMMEDIATE_KHR;
pDstModes[n++] = VK_PRESENT_MODE_MAILBOX_KHR;
}
return n;
}
uint32_t D3D9SwapChainEx::PickImageCount(
UINT Preferred) {
int32_t option = m_parent->GetOptions()->numBackBuffers;

View File

@ -155,9 +155,7 @@ namespace dxvk {
Rc<sync::Fence> m_frameLatencySignal;
bool m_dirty = true;
bool m_vsync = true;
bool m_dialog;
bool m_dialog = false;
bool m_lastDialog = false;
HWND m_window = nullptr;
@ -183,8 +181,7 @@ namespace dxvk {
void SynchronizePresent();
void RecreateSwapChain(
BOOL Vsync);
void RecreateSwapChain();
void CreatePresenter();
@ -209,10 +206,6 @@ namespace dxvk {
D3D9Format Format,
VkSurfaceFormatKHR* pDstFormats);
uint32_t PickPresentModes(
BOOL Vsync,
VkPresentModeKHR* pDstModes);
uint32_t PickImageCount(
UINT Preferred);

View File

@ -253,11 +253,13 @@ namespace dxvk {
void DxvkDevice::presentImage(
const Rc<Presenter>& presenter,
VkPresentModeKHR presentMode,
DxvkSubmitStatus* status) {
status->result = VK_NOT_READY;
DxvkPresentInfo presentInfo;
DxvkPresentInfo presentInfo = { };
presentInfo.presenter = presenter;
presentInfo.presentMode = presentMode;
m_submissionQueue.present(presentInfo, status);
std::lock_guard<sync::Spinlock> statLock(m_statLock);

View File

@ -454,10 +454,12 @@ namespace dxvk {
* the submission thread. The status of this operation
* can be retrieved with \ref waitForSubmission.
* \param [in] presenter The presenter
* \param [in] presenteMode Present mode
* \param [out] status Present status
*/
void presentImage(
const Rc<Presenter>& presenter,
VkPresentModeKHR presentMode,
DxvkSubmitStatus* status);
/**

View File

@ -49,7 +49,7 @@ namespace dxvk {
}
VkResult Presenter::presentImage() {
VkResult Presenter::presentImage(VkPresentModeKHR mode) {
PresenterSync sync = m_semaphores.at(m_frameIndex);
VkPresentInfoKHR info = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
@ -76,8 +76,8 @@ namespace dxvk {
m_swapchain, std::numeric_limits<uint64_t>::max(),
sync.acquire, VK_NULL_HANDLE, &m_imageIndex);
bool vsync = m_info.presentMode == VK_PRESENT_MODE_FIFO_KHR
|| m_info.presentMode == VK_PRESENT_MODE_FIFO_RELAXED_KHR;
bool vsync = mode == VK_PRESENT_MODE_FIFO_KHR
|| mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR;
m_fpsLimiter.delay(vsync);
return status;
@ -124,7 +124,7 @@ namespace dxvk {
// 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(), desc.numPresentModes, desc.presentModes);
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);
@ -234,6 +234,15 @@ namespace dxvk {
}
VkResult Presenter::setSyncInterval(uint32_t syncInterval) {
if (syncInterval == m_info.syncInterval)
return VK_SUCCESS;
m_info.syncInterval = syncInterval;
return VK_ERROR_OUT_OF_DATE_KHR;
}
void Presenter::setFrameRateLimit(double frameRate) {
m_fpsLimiter.setTargetFrameRate(frameRate);
}
@ -381,12 +390,19 @@ namespace dxvk {
VkPresentModeKHR Presenter::pickPresentMode(
uint32_t numSupported,
const VkPresentModeKHR* pSupported,
uint32_t numDesired,
const VkPresentModeKHR* pDesired) {
uint32_t syncInterval) {
std::array<VkPresentModeKHR, 2> desired = { };
uint32_t numDesired = 0;
if (!syncInterval) {
desired[numDesired++] = VK_PRESENT_MODE_IMMEDIATE_KHR;
desired[numDesired++] = VK_PRESENT_MODE_MAILBOX_KHR;
}
// Just pick the first desired and supported mode
for (uint32_t i = 0; i < numDesired; i++) {
for (uint32_t j = 0; j < numSupported; j++) {
if (pSupported[j] == pDesired[i])
if (pSupported[j] == desired[i])
return pSupported[j];
}
}

View File

@ -30,8 +30,6 @@ namespace dxvk {
uint32_t imageCount;
uint32_t numFormats;
VkSurfaceFormatKHR formats[4];
uint32_t numPresentModes;
VkPresentModeKHR presentModes[4];
VkFullScreenExclusiveEXT fullScreenExclusive;
};
@ -46,6 +44,7 @@ namespace dxvk {
VkPresentModeKHR presentMode;
VkExtent2D imageExtent;
uint32_t imageCount;
uint32_t syncInterval;
};
/**
@ -123,9 +122,11 @@ namespace dxvk {
* Presents the current image. If this returns
* an error, the swap chain must be recreated,
* but do not present before acquiring an image.
* \param [in] mode Present mode
* \returns Status of the operation
*/
VkResult presentImage();
VkResult presentImage(
VkPresentModeKHR mode);
/**
* \brief Changes and takes ownership of surface
@ -148,6 +149,15 @@ namespace dxvk {
VkResult recreateSwapChain(
const PresenterDesc& desc);
/**
* \brief Changes sync interval
*
* If this returns an error, the swap chain must
* be recreated.
* \param [in] syncInterval New sync interval
*/
VkResult setSyncInterval(uint32_t syncInterval);
/**
* \brief Changes maximum frame rate
*
@ -190,7 +200,7 @@ namespace dxvk {
Rc<vk::InstanceFn> m_vki;
Rc<vk::DeviceFn> m_vkd;
PresenterInfo m_info;
PresenterInfo m_info = { };
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
VkSwapchainKHR m_swapchain = VK_NULL_HANDLE;
@ -228,8 +238,7 @@ namespace dxvk {
VkPresentModeKHR pickPresentMode(
uint32_t numSupported,
const VkPresentModeKHR* pSupported,
uint32_t numDesired,
const VkPresentModeKHR* pDesired);
uint32_t syncInterval);
VkExtent2D pickImageExtent(
const VkSurfaceCapabilitiesKHR& caps,

View File

@ -118,7 +118,7 @@ namespace dxvk {
if (entry.submit.cmdList != nullptr)
status = entry.submit.cmdList->submit();
else if (entry.present.presenter != nullptr)
status = entry.present.presenter->presentImage();
status = entry.present.presenter->presentImage(entry.present.presentMode);
if (m_callback)
m_callback(false);

View File

@ -43,6 +43,7 @@ namespace dxvk {
*/
struct DxvkPresentInfo {
Rc<Presenter> presenter;
VkPresentModeKHR presentMode;
};