mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-03-06 20:58:37 +01:00
[dxvk] Move Vulkan swapchain management to backend
Massive cleanup reduce code duplication between D3D11 and D3D9, and introduce a sane path to pass data around. Implicit swap chain recreation is now entirely transparent to the frontends.
This commit is contained in:
parent
9764a1736f
commit
bc8b091036
10 changed files with 514 additions and 452 deletions
|
@ -174,11 +174,11 @@ namespace dxvk {
|
||||||
const DXGI_SWAP_CHAIN_DESC1* pDesc,
|
const DXGI_SWAP_CHAIN_DESC1* pDesc,
|
||||||
const UINT* pNodeMasks,
|
const UINT* pNodeMasks,
|
||||||
IUnknown* const* ppPresentQueues) {
|
IUnknown* const* ppPresentQueues) {
|
||||||
m_dirty |= m_desc.Format != pDesc->Format
|
if (m_desc.Format != pDesc->Format)
|
||||||
|| m_desc.Width != pDesc->Width
|
m_presenter->setSurfaceFormat(GetSurfaceFormat(pDesc->Format));
|
||||||
|| m_desc.Height != pDesc->Height
|
|
||||||
|| m_desc.BufferCount != pDesc->BufferCount
|
if (m_desc.Width != pDesc->Width || m_desc.Height != pDesc->Height)
|
||||||
|| m_desc.Flags != pDesc->Flags;
|
m_presenter->setSurfaceExtent({ m_desc.Width, m_desc.Height });
|
||||||
|
|
||||||
m_desc = *pDesc;
|
m_desc = *pDesc;
|
||||||
CreateBackBuffers();
|
CreateBackBuffers();
|
||||||
|
@ -251,33 +251,32 @@ namespace dxvk {
|
||||||
UINT SyncInterval,
|
UINT SyncInterval,
|
||||||
UINT PresentFlags,
|
UINT PresentFlags,
|
||||||
const DXGI_PRESENT_PARAMETERS* pPresentParameters) {
|
const DXGI_PRESENT_PARAMETERS* pPresentParameters) {
|
||||||
if (!(PresentFlags & DXGI_PRESENT_TEST))
|
|
||||||
m_dirty |= m_presenter->setSyncInterval(SyncInterval) != VK_SUCCESS;
|
|
||||||
|
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
|
|
||||||
if (!m_presenter->hasSwapChain()) {
|
|
||||||
RecreateSwapChain();
|
|
||||||
m_dirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_presenter->hasSwapChain())
|
|
||||||
hr = DXGI_STATUS_OCCLUDED;
|
|
||||||
|
|
||||||
if (m_device->getDeviceStatus() != VK_SUCCESS)
|
if (m_device->getDeviceStatus() != VK_SUCCESS)
|
||||||
hr = DXGI_ERROR_DEVICE_RESET;
|
hr = DXGI_ERROR_DEVICE_RESET;
|
||||||
|
|
||||||
if (PresentFlags & DXGI_PRESENT_TEST)
|
if (PresentFlags & DXGI_PRESENT_TEST) {
|
||||||
return hr;
|
if (hr != S_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
// If the current present status is NOT_READY, we have a present
|
||||||
|
// in flight, which means that we can most likely present again.
|
||||||
|
// This avoids an expensive sync point.
|
||||||
|
VkResult status = m_presentStatus.result.load();
|
||||||
|
|
||||||
|
if (status == VK_NOT_READY)
|
||||||
|
return S_OK;
|
||||||
|
|
||||||
|
status = m_presenter->checkSwapChainStatus();
|
||||||
|
return status == VK_SUCCESS ? S_OK : DXGI_STATUS_OCCLUDED;
|
||||||
|
}
|
||||||
|
|
||||||
if (hr != S_OK) {
|
if (hr != S_OK) {
|
||||||
SyncFrameLatency();
|
SyncFrameLatency();
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::exchange(m_dirty, false))
|
|
||||||
RecreateSwapChain();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
hr = PresentImage(SyncInterval);
|
hr = PresentImage(SyncInterval);
|
||||||
} catch (const DxvkError& e) {
|
} catch (const DxvkError& e) {
|
||||||
|
@ -298,7 +297,8 @@ namespace dxvk {
|
||||||
DXGI_COLOR_SPACE_TYPE ColorSpace) {
|
DXGI_COLOR_SPACE_TYPE ColorSpace) {
|
||||||
UINT supportFlags = 0;
|
UINT supportFlags = 0;
|
||||||
|
|
||||||
const VkColorSpaceKHR vkColorSpace = ConvertColorSpace(ColorSpace);
|
VkColorSpaceKHR vkColorSpace = ConvertColorSpace(ColorSpace);
|
||||||
|
|
||||||
if (m_presenter->supportsColorSpace(vkColorSpace))
|
if (m_presenter->supportsColorSpace(vkColorSpace))
|
||||||
supportFlags |= DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT;
|
supportFlags |= DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT;
|
||||||
|
|
||||||
|
@ -308,13 +308,14 @@ namespace dxvk {
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE D3D11SwapChain::SetColorSpace(
|
HRESULT STDMETHODCALLTYPE D3D11SwapChain::SetColorSpace(
|
||||||
DXGI_COLOR_SPACE_TYPE ColorSpace) {
|
DXGI_COLOR_SPACE_TYPE ColorSpace) {
|
||||||
if (!(CheckColorSpaceSupport(ColorSpace) & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT))
|
VkColorSpaceKHR colorSpace = ConvertColorSpace(ColorSpace);
|
||||||
|
|
||||||
|
if (!m_presenter->supportsColorSpace(colorSpace))
|
||||||
return E_INVALIDARG;
|
return E_INVALIDARG;
|
||||||
|
|
||||||
const VkColorSpaceKHR vkColorSpace = ConvertColorSpace(ColorSpace);
|
m_colorSpace = colorSpace;
|
||||||
m_dirty |= vkColorSpace != m_colorspace;
|
|
||||||
m_colorspace = vkColorSpace;
|
|
||||||
|
|
||||||
|
m_presenter->setSurfaceFormat(GetSurfaceFormat(m_desc.Format));
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,27 +379,19 @@ namespace dxvk {
|
||||||
|
|
||||||
SynchronizePresent();
|
SynchronizePresent();
|
||||||
|
|
||||||
if (!m_presenter->hasSwapChain())
|
m_presenter->setSyncInterval(SyncInterval);
|
||||||
return DXGI_STATUS_OCCLUDED;
|
|
||||||
|
|
||||||
// Presentation semaphores and WSI swap chain image
|
// Presentation semaphores and WSI swap chain image
|
||||||
PresenterSync sync;
|
PresenterSync sync;
|
||||||
|
|
||||||
Rc<DxvkImage> backBuffer;
|
Rc<DxvkImage> backBuffer;
|
||||||
|
|
||||||
VkResult status = m_presenter->acquireNextImage(sync, backBuffer);
|
VkResult status = m_presenter->acquireNextImage(sync, backBuffer);
|
||||||
|
|
||||||
while (status != VK_SUCCESS) {
|
if (status < 0)
|
||||||
RecreateSwapChain();
|
return E_FAIL;
|
||||||
|
|
||||||
if (!m_presenter->hasSwapChain())
|
if (status == VK_NOT_READY)
|
||||||
return DXGI_STATUS_OCCLUDED;
|
return DXGI_STATUS_OCCLUDED;
|
||||||
|
|
||||||
status = m_presenter->acquireNextImage(sync, backBuffer);
|
|
||||||
|
|
||||||
if (status == VK_SUBOPTIMAL_KHR)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_frameId += 1;
|
m_frameId += 1;
|
||||||
|
|
||||||
|
@ -425,7 +418,7 @@ namespace dxvk {
|
||||||
cSync = sync,
|
cSync = sync,
|
||||||
cHud = m_hud,
|
cHud = m_hud,
|
||||||
cPresenter = m_presenter,
|
cPresenter = m_presenter,
|
||||||
cColorSpace = m_colorspace,
|
cColorSpace = m_colorSpace,
|
||||||
cFrameId = m_frameId
|
cFrameId = m_frameId
|
||||||
] (DxvkContext* ctx) {
|
] (DxvkContext* ctx) {
|
||||||
// Blit the D3D back buffer onto the actual Vulkan
|
// Blit the D3D back buffer onto the actual Vulkan
|
||||||
|
@ -448,7 +441,6 @@ namespace dxvk {
|
||||||
ctx->flushCommandList(nullptr);
|
ctx->flushCommandList(nullptr);
|
||||||
|
|
||||||
cDevice->presentImage(cPresenter,
|
cDevice->presentImage(cPresenter,
|
||||||
cPresenter->info().presentMode,
|
|
||||||
cFrameId, cPresentStatus);
|
cFrameId, cPresentStatus);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -480,30 +472,7 @@ namespace dxvk {
|
||||||
|
|
||||||
|
|
||||||
void D3D11SwapChain::SynchronizePresent() {
|
void D3D11SwapChain::SynchronizePresent() {
|
||||||
// Recreate swap chain if the previous present call failed
|
|
||||||
VkResult status = m_device->waitForSubmission(&m_presentStatus);
|
|
||||||
|
|
||||||
if (status != VK_SUCCESS)
|
|
||||||
RecreateSwapChain();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void D3D11SwapChain::RecreateSwapChain() {
|
|
||||||
// Ensure that we can safely destroy the swap chain
|
|
||||||
m_device->waitForSubmission(&m_presentStatus);
|
m_device->waitForSubmission(&m_presentStatus);
|
||||||
m_device->waitForIdle();
|
|
||||||
|
|
||||||
m_presentStatus.result = VK_SUCCESS;
|
|
||||||
|
|
||||||
PresenterDesc presenterDesc;
|
|
||||||
presenterDesc.imageExtent = { m_desc.Width, m_desc.Height };
|
|
||||||
presenterDesc.imageCount = PickImageCount(m_desc.BufferCount + 1);
|
|
||||||
presenterDesc.numFormats = PickFormats(m_desc.Format, presenterDesc.formats);
|
|
||||||
|
|
||||||
VkResult vr = m_presenter->recreateSwapChain(presenterDesc);
|
|
||||||
|
|
||||||
if (vr)
|
|
||||||
throw DxvkError(str::format("D3D11SwapChain: Failed to recreate swap chain: ", vr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -516,10 +485,7 @@ namespace dxvk {
|
||||||
|
|
||||||
|
|
||||||
void D3D11SwapChain::CreatePresenter() {
|
void D3D11SwapChain::CreatePresenter() {
|
||||||
PresenterDesc presenterDesc;
|
PresenterDesc presenterDesc = { };
|
||||||
presenterDesc.imageExtent = { m_desc.Width, m_desc.Height };
|
|
||||||
presenterDesc.imageCount = PickImageCount(m_desc.BufferCount + 1);
|
|
||||||
presenterDesc.numFormats = PickFormats(m_desc.Format, presenterDesc.formats);
|
|
||||||
presenterDesc.deferSurfaceCreation = m_parent->GetOptions()->deferSurfaceCreation;
|
presenterDesc.deferSurfaceCreation = m_parent->GetOptions()->deferSurfaceCreation;
|
||||||
|
|
||||||
m_presenter = new Presenter(m_device, m_frameLatencySignal, presenterDesc, [
|
m_presenter = new Presenter(m_device, m_frameLatencySignal, presenterDesc, [
|
||||||
|
@ -531,6 +497,8 @@ namespace dxvk {
|
||||||
cAdapter->handle(), surface);
|
cAdapter->handle(), surface);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
m_presenter->setSurfaceFormat(GetSurfaceFormat(m_desc.Format));
|
||||||
|
m_presenter->setSurfaceExtent({ m_desc.Width, m_desc.Height });
|
||||||
m_presenter->setFrameRateLimit(m_targetFrameRate, GetActualFrameLatency());
|
m_presenter->setFrameRateLimit(m_targetFrameRate, GetActualFrameLatency());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -658,46 +626,26 @@ namespace dxvk {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint32_t D3D11SwapChain::PickFormats(
|
VkSurfaceFormatKHR D3D11SwapChain::GetSurfaceFormat(DXGI_FORMAT Format) {
|
||||||
DXGI_FORMAT Format,
|
|
||||||
VkSurfaceFormatKHR* pDstFormats) {
|
|
||||||
uint32_t n = 0;
|
|
||||||
|
|
||||||
switch (Format) {
|
switch (Format) {
|
||||||
default:
|
default:
|
||||||
Logger::warn(str::format("D3D11SwapChain: Unexpected format: ", m_desc.Format));
|
Logger::warn(str::format("D3D11SwapChain: Unexpected format: ", m_desc.Format));
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
|
|
||||||
case DXGI_FORMAT_R8G8B8A8_UNORM:
|
case DXGI_FORMAT_R8G8B8A8_UNORM:
|
||||||
case DXGI_FORMAT_B8G8R8A8_UNORM: {
|
case DXGI_FORMAT_B8G8R8A8_UNORM:
|
||||||
pDstFormats[n++] = { VK_FORMAT_R8G8B8A8_UNORM, m_colorspace };
|
return { VK_FORMAT_R8G8B8A8_UNORM, m_colorSpace };
|
||||||
pDstFormats[n++] = { VK_FORMAT_B8G8R8A8_UNORM, m_colorspace };
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
|
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
|
||||||
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: {
|
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
|
||||||
pDstFormats[n++] = { VK_FORMAT_R8G8B8A8_SRGB, m_colorspace };
|
return { VK_FORMAT_R8G8B8A8_SRGB, m_colorSpace };
|
||||||
pDstFormats[n++] = { VK_FORMAT_B8G8R8A8_SRGB, m_colorspace };
|
|
||||||
} break;
|
case DXGI_FORMAT_R10G10B10A2_UNORM:
|
||||||
|
return { VK_FORMAT_A2B10G10R10_UNORM_PACK32, m_colorSpace };
|
||||||
case DXGI_FORMAT_R10G10B10A2_UNORM: {
|
|
||||||
pDstFormats[n++] = { VK_FORMAT_A2B10G10R10_UNORM_PACK32, m_colorspace };
|
case DXGI_FORMAT_R16G16B16A16_FLOAT:
|
||||||
pDstFormats[n++] = { VK_FORMAT_A2R10G10B10_UNORM_PACK32, m_colorspace };
|
return { VK_FORMAT_R16G16B16A16_SFLOAT, m_colorSpace };
|
||||||
} break;
|
|
||||||
|
|
||||||
case DXGI_FORMAT_R16G16B16A16_FLOAT: {
|
|
||||||
pDstFormats[n++] = { VK_FORMAT_R16G16B16A16_SFLOAT, m_colorspace };
|
|
||||||
} break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
uint32_t D3D11SwapChain::PickImageCount(
|
|
||||||
UINT Preferred) {
|
|
||||||
int32_t option = m_parent->GetOptions()->numBackBuffers;
|
|
||||||
return option > 0 ? uint32_t(option) : uint32_t(Preferred);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -119,9 +119,7 @@ namespace dxvk {
|
||||||
HANDLE m_frameLatencyEvent = nullptr;
|
HANDLE m_frameLatencyEvent = nullptr;
|
||||||
Rc<sync::CallbackFence> m_frameLatencySignal;
|
Rc<sync::CallbackFence> m_frameLatencySignal;
|
||||||
|
|
||||||
bool m_dirty = true;
|
VkColorSpaceKHR m_colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||||
|
|
||||||
VkColorSpaceKHR m_colorspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
|
||||||
|
|
||||||
double m_targetFrameRate = 0.0;
|
double m_targetFrameRate = 0.0;
|
||||||
|
|
||||||
|
@ -136,8 +134,6 @@ namespace dxvk {
|
||||||
|
|
||||||
void SynchronizePresent();
|
void SynchronizePresent();
|
||||||
|
|
||||||
void RecreateSwapChain();
|
|
||||||
|
|
||||||
void CreateFrameLatencyEvent();
|
void CreateFrameLatencyEvent();
|
||||||
|
|
||||||
void CreatePresenter();
|
void CreatePresenter();
|
||||||
|
@ -154,12 +150,7 @@ namespace dxvk {
|
||||||
|
|
||||||
uint32_t GetActualFrameLatency();
|
uint32_t GetActualFrameLatency();
|
||||||
|
|
||||||
uint32_t PickFormats(
|
VkSurfaceFormatKHR GetSurfaceFormat(DXGI_FORMAT Format);
|
||||||
DXGI_FORMAT Format,
|
|
||||||
VkSurfaceFormatKHR* pDstFormats);
|
|
||||||
|
|
||||||
uint32_t PickImageCount(
|
|
||||||
UINT Preferred);
|
|
||||||
|
|
||||||
std::string GetApiName() const;
|
std::string GetApiName() const;
|
||||||
|
|
||||||
|
|
|
@ -153,18 +153,16 @@ namespace dxvk {
|
||||||
|
|
||||||
UpdateWindowCtx();
|
UpdateWindowCtx();
|
||||||
|
|
||||||
bool recreate = false;
|
bool recreate = !m_wctx->presenter;
|
||||||
recreate |= m_wctx->presenter == nullptr;
|
|
||||||
if (options->deferSurfaceCreation)
|
if (options->deferSurfaceCreation)
|
||||||
recreate |= m_parent->IsDeviceReset();
|
recreate |= m_parent->IsDeviceReset();
|
||||||
|
|
||||||
if (m_wctx->presenter != nullptr) {
|
if (m_wctx->presenter)
|
||||||
m_dirty |= m_wctx->presenter->setSyncInterval(presentInterval) != VK_SUCCESS;
|
m_wctx->presenter->setSyncInterval(presentInterval);
|
||||||
m_dirty |= !m_wctx->presenter->hasSwapChain();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_dirty |= UpdatePresentRegion(pSourceRect, pDestRect);
|
UpdatePresentRegion(pSourceRect, pDestRect);
|
||||||
m_dirty |= recreate;
|
UpdatePresentParameters();
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
const bool useGDIFallback = m_partialCopy && !HasFrontBuffer();
|
const bool useGDIFallback = m_partialCopy && !HasFrontBuffer();
|
||||||
|
@ -174,17 +172,11 @@ namespace dxvk {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (recreate)
|
if (recreate)
|
||||||
CreatePresenter();
|
RecreateSurface();
|
||||||
|
|
||||||
if (std::exchange(m_dirty, false))
|
|
||||||
RecreateSwapChain();
|
|
||||||
|
|
||||||
// We aren't going to device loss simply because
|
// We aren't going to device loss simply because
|
||||||
// 99% of D3D9 games don't handle this properly and
|
// 99% of D3D9 games don't handle this properly and
|
||||||
// just end up crashing (like with alt-tab loss)
|
// just end up crashing (like with alt-tab loss)
|
||||||
if (!m_wctx->presenter->hasSwapChain())
|
|
||||||
return D3D_OK;
|
|
||||||
|
|
||||||
UpdateTargetFrameRate(presentInterval);
|
UpdateTargetFrameRate(presentInterval);
|
||||||
PresentImage(presentInterval);
|
PresentImage(presentInterval);
|
||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
|
@ -606,9 +598,6 @@ namespace dxvk {
|
||||||
this->SynchronizePresent();
|
this->SynchronizePresent();
|
||||||
this->NormalizePresentParameters(pPresentParams);
|
this->NormalizePresentParameters(pPresentParams);
|
||||||
|
|
||||||
m_dirty |= m_presentParams.BackBufferFormat != pPresentParams->BackBufferFormat
|
|
||||||
|| m_presentParams.BackBufferCount != pPresentParams->BackBufferCount;
|
|
||||||
|
|
||||||
bool changeFullscreen = m_presentParams.Windowed != pPresentParams->Windowed;
|
bool changeFullscreen = m_presentParams.Windowed != pPresentParams->Windowed;
|
||||||
|
|
||||||
if (pPresentParams->Windowed) {
|
if (pPresentParams->Windowed) {
|
||||||
|
@ -640,6 +629,8 @@ namespace dxvk {
|
||||||
if (changeFullscreen)
|
if (changeFullscreen)
|
||||||
SetGammaRamp(0, &m_ramp);
|
SetGammaRamp(0, &m_ramp);
|
||||||
|
|
||||||
|
UpdatePresentParameters();
|
||||||
|
|
||||||
hr = CreateBackBuffers(m_presentParams.BackBufferCount, m_presentParams.Flags);
|
hr = CreateBackBuffers(m_presentParams.BackBufferCount, m_presentParams.Flags);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
return hr;
|
return hr;
|
||||||
|
@ -826,27 +817,13 @@ namespace dxvk {
|
||||||
SynchronizePresent();
|
SynchronizePresent();
|
||||||
|
|
||||||
// Presentation semaphores and WSI swap chain image
|
// Presentation semaphores and WSI swap chain image
|
||||||
PresenterInfo info = m_wctx->presenter->info();
|
|
||||||
PresenterSync sync = { };
|
PresenterSync sync = { };
|
||||||
|
|
||||||
Rc<DxvkImage> backBuffer;
|
Rc<DxvkImage> backBuffer;
|
||||||
|
|
||||||
VkResult status = m_wctx->presenter->acquireNextImage(sync, backBuffer);
|
VkResult status = m_wctx->presenter->acquireNextImage(sync, backBuffer);
|
||||||
|
|
||||||
while (status != VK_SUCCESS) {
|
if (status < 0 || status == VK_NOT_READY)
|
||||||
RecreateSwapChain();
|
break;
|
||||||
|
|
||||||
info = m_wctx->presenter->info();
|
|
||||||
status = m_wctx->presenter->acquireNextImage(sync, backBuffer);
|
|
||||||
|
|
||||||
if (status == VK_SUBOPTIMAL_KHR)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_hdrMetadata && m_dirtyHdrMetadata) {
|
|
||||||
m_wctx->presenter->setHdrMetadata(*m_hdrMetadata);
|
|
||||||
m_dirtyHdrMetadata = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
VkRect2D srcRect = {
|
VkRect2D srcRect = {
|
||||||
{ int32_t(m_srcRect.left), int32_t(m_srcRect.top) },
|
{ int32_t(m_srcRect.left), int32_t(m_srcRect.top) },
|
||||||
|
@ -912,7 +889,6 @@ namespace dxvk {
|
||||||
uint64_t frameId = cRepeat ? 0 : cFrameId;
|
uint64_t frameId = cRepeat ? 0 : cFrameId;
|
||||||
|
|
||||||
cDevice->presentImage(cPresenter,
|
cDevice->presentImage(cPresenter,
|
||||||
cPresenter->info().presentMode,
|
|
||||||
frameId, cPresentStatus);
|
frameId, cPresentStatus);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -931,29 +907,15 @@ namespace dxvk {
|
||||||
|
|
||||||
|
|
||||||
void D3D9SwapChainEx::SynchronizePresent() {
|
void D3D9SwapChainEx::SynchronizePresent() {
|
||||||
// Recreate swap chain if the previous present call failed
|
m_device->waitForSubmission(&m_presentStatus);
|
||||||
VkResult status = m_device->waitForSubmission(&m_presentStatus);
|
|
||||||
|
|
||||||
if (status != VK_SUCCESS)
|
|
||||||
RecreateSwapChain();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D9SwapChainEx::RecreateSwapChain() {
|
|
||||||
// Ensure that we can safely destroy the swap chain
|
|
||||||
m_device->waitForSubmission(&m_presentStatus);
|
|
||||||
m_device->waitForIdle();
|
|
||||||
|
|
||||||
m_presentStatus.result = VK_SUCCESS;
|
void D3D9SwapChainEx::RecreateSurface() {
|
||||||
|
if (m_wctx->presenter)
|
||||||
PresenterDesc presenterDesc;
|
m_wctx->presenter->invalidateSurface();
|
||||||
presenterDesc.imageExtent = GetPresentExtent();
|
else
|
||||||
presenterDesc.imageCount = PickImageCount(m_presentParams.BackBufferCount + 1);
|
CreatePresenter();
|
||||||
presenterDesc.numFormats = PickFormats(EnumerateFormat(m_presentParams.BackBufferFormat), presenterDesc.formats);
|
|
||||||
|
|
||||||
VkResult vr = m_wctx->presenter->recreateSwapChain(presenterDesc);
|
|
||||||
|
|
||||||
if (vr)
|
|
||||||
throw DxvkError(str::format("D3D9SwapChainEx: Failed to recreate swap chain: ", vr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -965,9 +927,6 @@ namespace dxvk {
|
||||||
m_presentStatus.result = VK_SUCCESS;
|
m_presentStatus.result = VK_SUCCESS;
|
||||||
|
|
||||||
PresenterDesc presenterDesc;
|
PresenterDesc presenterDesc;
|
||||||
presenterDesc.imageExtent = GetPresentExtent();
|
|
||||||
presenterDesc.imageCount = PickImageCount(m_presentParams.BackBufferCount + 1);
|
|
||||||
presenterDesc.numFormats = PickFormats(EnumerateFormat(m_presentParams.BackBufferFormat), presenterDesc.formats);
|
|
||||||
presenterDesc.deferSurfaceCreation = m_parent->GetOptions()->deferSurfaceCreation;
|
presenterDesc.deferSurfaceCreation = m_parent->GetOptions()->deferSurfaceCreation;
|
||||||
|
|
||||||
m_wctx->presenter = new Presenter(m_device,
|
m_wctx->presenter = new Presenter(m_device,
|
||||||
|
@ -982,6 +941,12 @@ namespace dxvk {
|
||||||
vki->instance(),
|
vki->instance(),
|
||||||
surface);
|
surface);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
m_wctx->presenter->setSurfaceExtent(m_swapchainExtent);
|
||||||
|
m_wctx->presenter->setSurfaceFormat(GetSurfaceFormat());
|
||||||
|
|
||||||
|
if (m_hdrMetadata)
|
||||||
|
m_wctx->presenter->setHdrMetadata(*m_hdrMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1138,60 +1103,44 @@ namespace dxvk {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint32_t D3D9SwapChainEx::PickFormats(
|
VkSurfaceFormatKHR D3D9SwapChainEx::GetSurfaceFormat() {
|
||||||
D3D9Format Format,
|
D3D9Format format = EnumerateFormat(m_presentParams.BackBufferFormat);
|
||||||
VkSurfaceFormatKHR* pDstFormats) {
|
|
||||||
uint32_t n = 0;
|
|
||||||
|
|
||||||
switch (Format) {
|
switch (format) {
|
||||||
default:
|
default:
|
||||||
Logger::warn(str::format("D3D9SwapChainEx: Unexpected format: ", Format));
|
Logger::warn(str::format("D3D9SwapChainEx: Unexpected format: ", format));
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
|
|
||||||
case D3D9Format::A8R8G8B8:
|
case D3D9Format::A8R8G8B8:
|
||||||
case D3D9Format::X8R8G8B8:
|
case D3D9Format::X8R8G8B8:
|
||||||
|
return { VK_FORMAT_B8G8R8A8_UNORM, m_colorspace };
|
||||||
|
|
||||||
case D3D9Format::A8B8G8R8:
|
case D3D9Format::A8B8G8R8:
|
||||||
case D3D9Format::X8B8G8R8: {
|
case D3D9Format::X8B8G8R8:
|
||||||
pDstFormats[n++] = { VK_FORMAT_R8G8B8A8_UNORM, m_colorspace };
|
return { VK_FORMAT_R8G8B8A8_UNORM, m_colorspace };
|
||||||
pDstFormats[n++] = { VK_FORMAT_B8G8R8A8_UNORM, m_colorspace };
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case D3D9Format::A2R10G10B10:
|
case D3D9Format::A2R10G10B10:
|
||||||
case D3D9Format::A2B10G10R10: {
|
return { VK_FORMAT_A2R10G10B10_UNORM_PACK32, m_colorspace };
|
||||||
pDstFormats[n++] = { VK_FORMAT_A2B10G10R10_UNORM_PACK32, m_colorspace };
|
|
||||||
pDstFormats[n++] = { VK_FORMAT_A2R10G10B10_UNORM_PACK32, m_colorspace };
|
case D3D9Format::A2B10G10R10:
|
||||||
} break;
|
return { VK_FORMAT_A2B10G10R10_UNORM_PACK32, m_colorspace };
|
||||||
|
|
||||||
case D3D9Format::X1R5G5B5:
|
case D3D9Format::X1R5G5B5:
|
||||||
case D3D9Format::A1R5G5B5: {
|
case D3D9Format::A1R5G5B5:
|
||||||
pDstFormats[n++] = { VK_FORMAT_B5G5R5A1_UNORM_PACK16, m_colorspace };
|
return { VK_FORMAT_B5G5R5A1_UNORM_PACK16, m_colorspace };
|
||||||
pDstFormats[n++] = { VK_FORMAT_R5G5B5A1_UNORM_PACK16, m_colorspace };
|
|
||||||
pDstFormats[n++] = { VK_FORMAT_A1R5G5B5_UNORM_PACK16, m_colorspace };
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case D3D9Format::R5G6B5: {
|
case D3D9Format::R5G6B5:
|
||||||
pDstFormats[n++] = { VK_FORMAT_B5G6R5_UNORM_PACK16, m_colorspace };
|
return { VK_FORMAT_B5G6R5_UNORM_PACK16, m_colorspace };
|
||||||
pDstFormats[n++] = { VK_FORMAT_R5G6B5_UNORM_PACK16, m_colorspace };
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case D3D9Format::A16B16G16R16F: {
|
case D3D9Format::A16B16G16R16F: {
|
||||||
if (m_unlockAdditionalFormats) {
|
if (!m_unlockAdditionalFormats) {
|
||||||
pDstFormats[n++] = { VK_FORMAT_R16G16B16A16_SFLOAT, m_colorspace };
|
Logger::warn(str::format("D3D9SwapChainEx: Unexpected format: ", format));
|
||||||
} else {
|
return VkSurfaceFormatKHR { };
|
||||||
Logger::warn(str::format("D3D9SwapChainEx: Unexpected format: ", Format));
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
return { VK_FORMAT_R16G16B16A16_SFLOAT, m_colorspace };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
uint32_t D3D9SwapChainEx::PickImageCount(
|
|
||||||
UINT Preferred) {
|
|
||||||
int32_t option = m_parent->GetOptions()->numBackBuffers;
|
|
||||||
return option > 0 ? uint32_t(option) : uint32_t(Preferred);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1297,7 +1246,7 @@ namespace dxvk {
|
||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D9SwapChainEx::UpdatePresentRegion(const RECT* pSourceRect, const RECT* pDestRect) {
|
void D3D9SwapChainEx::UpdatePresentRegion(const RECT* pSourceRect, const RECT* pDestRect) {
|
||||||
const bool isWindowed = m_presentParams.Windowed;
|
const bool isWindowed = m_presentParams.Windowed;
|
||||||
|
|
||||||
// Tests show that present regions are ignored in fullscreen
|
// Tests show that present regions are ignored in fullscreen
|
||||||
|
@ -1333,15 +1282,15 @@ namespace dxvk {
|
||||||
|| dstRect.right - dstRect.left != LONG(width)
|
|| dstRect.right - dstRect.left != LONG(width)
|
||||||
|| dstRect.bottom - dstRect.top != LONG(height);
|
|| dstRect.bottom - dstRect.top != LONG(height);
|
||||||
|
|
||||||
bool recreate = m_wctx != nullptr
|
|
||||||
&& (m_wctx->presenter == nullptr
|
|
||||||
|| m_wctx->presenter->info().imageExtent.width != width
|
|
||||||
|| m_wctx->presenter->info().imageExtent.height != height);
|
|
||||||
|
|
||||||
m_swapchainExtent = { width, height };
|
m_swapchainExtent = { width, height };
|
||||||
m_dstRect = dstRect;
|
m_dstRect = dstRect;
|
||||||
|
}
|
||||||
|
|
||||||
return recreate;
|
void D3D9SwapChainEx::UpdatePresentParameters() {
|
||||||
|
if (m_wctx && m_wctx->presenter) {
|
||||||
|
m_wctx->presenter->setSurfaceExtent(m_swapchainExtent);
|
||||||
|
m_wctx->presenter->setSurfaceFormat(GetSurfaceFormat());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VkExtent2D D3D9SwapChainEx::GetPresentExtent() {
|
VkExtent2D D3D9SwapChainEx::GetPresentExtent() {
|
||||||
|
@ -1386,9 +1335,11 @@ namespace dxvk {
|
||||||
if (!CheckColorSpaceSupport(ColorSpace))
|
if (!CheckColorSpaceSupport(ColorSpace))
|
||||||
return D3DERR_INVALIDCALL;
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
m_swapchain->m_dirty |= ColorSpace != m_swapchain->m_colorspace;
|
|
||||||
m_swapchain->m_colorspace = ColorSpace;
|
m_swapchain->m_colorspace = ColorSpace;
|
||||||
|
|
||||||
|
if (m_swapchain->m_wctx && m_swapchain->m_wctx->presenter)
|
||||||
|
m_swapchain->m_wctx->presenter->setSurfaceFormat(m_swapchain->GetSurfaceFormat());
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1397,8 +1348,10 @@ namespace dxvk {
|
||||||
if (!pHDRMetadata)
|
if (!pHDRMetadata)
|
||||||
return D3DERR_INVALIDCALL;
|
return D3DERR_INVALIDCALL;
|
||||||
|
|
||||||
m_swapchain->m_hdrMetadata = *pHDRMetadata;
|
m_swapchain->m_hdrMetadata = *pHDRMetadata;
|
||||||
m_swapchain->m_dirtyHdrMetadata = true;
|
|
||||||
|
if (m_swapchain->m_wctx && m_swapchain->m_wctx->presenter)
|
||||||
|
m_swapchain->m_wctx->presenter->setHdrMetadata(*pHDRMetadata);
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,8 +169,6 @@ namespace dxvk {
|
||||||
|
|
||||||
uint32_t m_frameLatencyCap = 0;
|
uint32_t m_frameLatencyCap = 0;
|
||||||
|
|
||||||
bool m_dirty = true;
|
|
||||||
|
|
||||||
HWND m_window = nullptr;
|
HWND m_window = nullptr;
|
||||||
HMONITOR m_monitor = nullptr;
|
HMONITOR m_monitor = nullptr;
|
||||||
|
|
||||||
|
@ -185,7 +183,6 @@ namespace dxvk {
|
||||||
VkColorSpaceKHR m_colorspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
VkColorSpaceKHR m_colorspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||||
|
|
||||||
std::optional<VkHdrMetadataEXT> m_hdrMetadata;
|
std::optional<VkHdrMetadataEXT> m_hdrMetadata;
|
||||||
bool m_dirtyHdrMetadata = true;
|
|
||||||
bool m_unlockAdditionalFormats = false;
|
bool m_unlockAdditionalFormats = false;
|
||||||
|
|
||||||
D3D9VkExtSwapchain m_swapchainExt;
|
D3D9VkExtSwapchain m_swapchainExt;
|
||||||
|
@ -194,7 +191,7 @@ namespace dxvk {
|
||||||
|
|
||||||
void SynchronizePresent();
|
void SynchronizePresent();
|
||||||
|
|
||||||
void RecreateSwapChain();
|
void RecreateSurface();
|
||||||
|
|
||||||
void CreatePresenter();
|
void CreatePresenter();
|
||||||
|
|
||||||
|
@ -212,13 +209,8 @@ namespace dxvk {
|
||||||
|
|
||||||
uint32_t GetActualFrameLatency();
|
uint32_t GetActualFrameLatency();
|
||||||
|
|
||||||
uint32_t PickFormats(
|
VkSurfaceFormatKHR GetSurfaceFormat();
|
||||||
D3D9Format Format,
|
|
||||||
VkSurfaceFormatKHR* pDstFormats);
|
|
||||||
|
|
||||||
uint32_t PickImageCount(
|
|
||||||
UINT Preferred);
|
|
||||||
|
|
||||||
void NormalizePresentParameters(D3DPRESENT_PARAMETERS* pPresentParams);
|
void NormalizePresentParameters(D3DPRESENT_PARAMETERS* pPresentParams);
|
||||||
|
|
||||||
void NotifyDisplayRefreshRate(
|
void NotifyDisplayRefreshRate(
|
||||||
|
@ -236,7 +228,9 @@ namespace dxvk {
|
||||||
|
|
||||||
HRESULT RestoreDisplayMode(HMONITOR hMonitor);
|
HRESULT RestoreDisplayMode(HMONITOR hMonitor);
|
||||||
|
|
||||||
bool UpdatePresentRegion(const RECT* pSourceRect, const RECT* pDestRect);
|
void UpdatePresentRegion(const RECT* pSourceRect, const RECT* pDestRect);
|
||||||
|
|
||||||
|
void UpdatePresentParameters();
|
||||||
|
|
||||||
VkExtent2D GetPresentExtent();
|
VkExtent2D GetPresentExtent();
|
||||||
|
|
||||||
|
|
|
@ -307,14 +307,12 @@ namespace dxvk {
|
||||||
|
|
||||||
void DxvkDevice::presentImage(
|
void DxvkDevice::presentImage(
|
||||||
const Rc<Presenter>& presenter,
|
const Rc<Presenter>& presenter,
|
||||||
VkPresentModeKHR presentMode,
|
|
||||||
uint64_t frameId,
|
uint64_t frameId,
|
||||||
DxvkSubmitStatus* status) {
|
DxvkSubmitStatus* status) {
|
||||||
status->result = VK_NOT_READY;
|
status->result = VK_NOT_READY;
|
||||||
|
|
||||||
DxvkPresentInfo presentInfo = { };
|
DxvkPresentInfo presentInfo = { };
|
||||||
presentInfo.presenter = presenter;
|
presentInfo.presenter = presenter;
|
||||||
presentInfo.presentMode = presentMode;
|
|
||||||
presentInfo.frameId = frameId;
|
presentInfo.frameId = frameId;
|
||||||
m_submissionQueue.present(presentInfo, status);
|
m_submissionQueue.present(presentInfo, status);
|
||||||
|
|
||||||
|
|
|
@ -485,13 +485,11 @@ namespace dxvk {
|
||||||
* the submission thread. The status of this operation
|
* the submission thread. The status of this operation
|
||||||
* can be retrieved with \ref waitForSubmission.
|
* can be retrieved with \ref waitForSubmission.
|
||||||
* \param [in] presenter The presenter
|
* \param [in] presenter The presenter
|
||||||
* \param [in] presenteMode Present mode
|
|
||||||
* \param [in] frameId Optional frame ID
|
* \param [in] frameId Optional frame ID
|
||||||
* \param [out] status Present status
|
* \param [out] status Present status
|
||||||
*/
|
*/
|
||||||
void presentImage(
|
void presentImage(
|
||||||
const Rc<Presenter>& presenter,
|
const Rc<Presenter>& presenter,
|
||||||
VkPresentModeKHR presentMode,
|
|
||||||
uint64_t frameId,
|
uint64_t frameId,
|
||||||
DxvkSubmitStatus* status);
|
DxvkSubmitStatus* status);
|
||||||
|
|
||||||
|
|
|
@ -52,26 +52,51 @@ namespace dxvk {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PresenterInfo Presenter::info() const {
|
VkResult Presenter::checkSwapChainStatus() {
|
||||||
return m_info;
|
std::lock_guard lock(m_surfaceMutex);
|
||||||
|
|
||||||
|
if (!m_swapchain)
|
||||||
|
return recreateSwapChain();
|
||||||
|
|
||||||
|
return VK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
VkResult Presenter::acquireNextImage(PresenterSync& sync, Rc<DxvkImage>& image) {
|
VkResult Presenter::acquireNextImage(PresenterSync& sync, Rc<DxvkImage>& image) {
|
||||||
PresenterSync& semaphores = m_semaphores.at(m_frameIndex);
|
std::lock_guard lock(m_surfaceMutex);
|
||||||
sync = semaphores;
|
|
||||||
|
// Ensure that the swap chain gets recreated if it is dirty
|
||||||
|
updateSwapChain();
|
||||||
|
|
||||||
// Don't acquire more than one image at a time
|
// Don't acquire more than one image at a time
|
||||||
if (m_acquireStatus == VK_NOT_READY) {
|
if (m_acquireStatus == VK_NOT_READY && m_swapchain) {
|
||||||
waitForSwapchainFence(semaphores);
|
PresenterSync sync = m_semaphores.at(m_frameIndex);
|
||||||
|
|
||||||
|
waitForSwapchainFence(sync);
|
||||||
|
|
||||||
m_acquireStatus = m_vkd->vkAcquireNextImageKHR(m_vkd->device(),
|
m_acquireStatus = m_vkd->vkAcquireNextImageKHR(m_vkd->device(),
|
||||||
m_swapchain, std::numeric_limits<uint64_t>::max(),
|
m_swapchain, std::numeric_limits<uint64_t>::max(),
|
||||||
sync.acquire, VK_NULL_HANDLE, &m_imageIndex);
|
sync.acquire, VK_NULL_HANDLE, &m_imageIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_acquireStatus != VK_SUCCESS && m_acquireStatus != VK_SUBOPTIMAL_KHR)
|
// If the swap chain is out of date, recreate it and retry. It
|
||||||
return m_acquireStatus;
|
// is possible that we do not get a new swap chain here, e.g.
|
||||||
|
// because the window is minimized.
|
||||||
|
if (m_acquireStatus != VK_SUCCESS || !m_swapchain) {
|
||||||
|
VkResult vr = recreateSwapChain();
|
||||||
|
|
||||||
|
if (vr != VK_SUCCESS)
|
||||||
|
return vr;
|
||||||
|
|
||||||
|
PresenterSync sync = m_semaphores.at(m_frameIndex);
|
||||||
|
|
||||||
|
m_acquireStatus = m_vkd->vkAcquireNextImageKHR(m_vkd->device(),
|
||||||
|
m_swapchain, std::numeric_limits<uint64_t>::max(),
|
||||||
|
sync.acquire, VK_NULL_HANDLE, &m_imageIndex);
|
||||||
|
|
||||||
|
if (m_acquireStatus < 0)
|
||||||
|
return m_acquireStatus;
|
||||||
|
}
|
||||||
|
|
||||||
// Update HDR metadata after a successful acquire. We know
|
// Update HDR metadata after a successful acquire. We know
|
||||||
// that there won't be a present in flight at this point.
|
// that there won't be a present in flight at this point.
|
||||||
|
@ -84,14 +109,19 @@ namespace dxvk {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set dynamic present mode for the next frame if possible
|
||||||
|
if (!m_dynamicModes.empty())
|
||||||
|
m_presentMode = m_dynamicModes.at(m_preferredSyncInterval ? 1u : 0u);
|
||||||
|
|
||||||
|
// Return relevant Vulkan objects for the acquired image
|
||||||
|
sync = m_semaphores.at(m_frameIndex);
|
||||||
image = m_images.at(m_imageIndex);
|
image = m_images.at(m_imageIndex);
|
||||||
|
|
||||||
return m_acquireStatus;
|
return m_acquireStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
VkResult Presenter::presentImage(
|
VkResult Presenter::presentImage(uint64_t frameId) {
|
||||||
VkPresentModeKHR mode,
|
|
||||||
uint64_t frameId) {
|
|
||||||
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 };
|
||||||
|
@ -104,7 +134,7 @@ namespace dxvk {
|
||||||
|
|
||||||
VkSwapchainPresentModeInfoEXT modeInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT };
|
VkSwapchainPresentModeInfoEXT modeInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT };
|
||||||
modeInfo.swapchainCount = 1;
|
modeInfo.swapchainCount = 1;
|
||||||
modeInfo.pPresentModes = &mode;
|
modeInfo.pPresentModes = &m_presentMode;
|
||||||
|
|
||||||
VkPresentInfoKHR info = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
|
VkPresentInfoKHR info = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
|
||||||
info.waitSemaphoreCount = 1;
|
info.waitSemaphoreCount = 1;
|
||||||
|
@ -146,10 +176,7 @@ namespace dxvk {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Presenter::signalFrame(
|
void Presenter::signalFrame(VkResult result, uint64_t frameId) {
|
||||||
VkResult result,
|
|
||||||
VkPresentModeKHR mode,
|
|
||||||
uint64_t frameId) {
|
|
||||||
if (m_signal == nullptr || !frameId)
|
if (m_signal == nullptr || !frameId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -158,7 +185,7 @@ namespace dxvk {
|
||||||
|
|
||||||
PresenterFrame frame = { };
|
PresenterFrame frame = { };
|
||||||
frame.result = result;
|
frame.result = result;
|
||||||
frame.mode = mode;
|
frame.mode = m_presentMode;
|
||||||
frame.frameId = frameId;
|
frame.frameId = frameId;
|
||||||
|
|
||||||
m_frameQueue.push(frame);
|
m_frameQueue.push(frame);
|
||||||
|
@ -172,14 +199,102 @@ namespace dxvk {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
VkResult Presenter::recreateSwapChain(const PresenterDesc& desc) {
|
bool Presenter::supportsColorSpace(VkColorSpaceKHR colorspace) {
|
||||||
|
std::lock_guard lock(m_surfaceMutex);
|
||||||
|
|
||||||
|
if (!m_surface) {
|
||||||
|
VkResult vr = createSurface();
|
||||||
|
|
||||||
|
if (vr != VK_SUCCESS)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VkSurfaceFormatKHR> surfaceFormats;
|
||||||
|
getSupportedFormats(surfaceFormats);
|
||||||
|
|
||||||
|
for (const auto& surfaceFormat : surfaceFormats) {
|
||||||
|
if (surfaceFormat.colorSpace == colorspace)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Presenter::invalidateSurface() {
|
||||||
|
std::lock_guard lock(m_surfaceMutex);
|
||||||
|
|
||||||
|
m_dirtySurface = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Presenter::setSyncInterval(uint32_t syncInterval) {
|
||||||
|
std::lock_guard lock(m_surfaceMutex);
|
||||||
|
|
||||||
|
// Normalize sync interval for present modes. We currently
|
||||||
|
// cannot support anything other than 1 natively anyway.
|
||||||
|
syncInterval = std::min(syncInterval, 1u);
|
||||||
|
|
||||||
|
if (m_preferredSyncInterval != syncInterval) {
|
||||||
|
m_preferredSyncInterval = syncInterval;
|
||||||
|
|
||||||
|
if (m_dynamicModes.empty())
|
||||||
|
m_dirtySwapchain = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Presenter::setFrameRateLimit(double frameRate, uint32_t maxLatency) {
|
||||||
|
m_fpsLimiter.setTargetFrameRate(frameRate, maxLatency);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Presenter::setSurfaceFormat(VkSurfaceFormatKHR format) {
|
||||||
|
std::lock_guard lock(m_surfaceMutex);
|
||||||
|
|
||||||
|
if (m_preferredFormat.format != format.format || m_preferredFormat.colorSpace != format.colorSpace) {
|
||||||
|
m_preferredFormat = format;
|
||||||
|
m_dirtySwapchain = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Presenter::setSurfaceExtent(VkExtent2D extent) {
|
||||||
|
std::lock_guard lock(m_surfaceMutex);
|
||||||
|
|
||||||
|
if (m_preferredExtent != extent) {
|
||||||
|
m_preferredExtent = extent;
|
||||||
|
m_dirtySwapchain = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Presenter::setHdrMetadata(VkHdrMetadataEXT hdrMetadata) {
|
||||||
|
std::lock_guard lock(m_surfaceMutex);
|
||||||
|
|
||||||
|
if (m_hdrMetadata->sType != VK_STRUCTURE_TYPE_HDR_METADATA_EXT) {
|
||||||
|
m_hdrMetadata = std::nullopt;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hdrMetadata.pNext)
|
||||||
|
Logger::warn("HDR metadata extensions not currently supported.");
|
||||||
|
|
||||||
|
m_hdrMetadata = hdrMetadata;
|
||||||
|
m_hdrMetadata->pNext = nullptr;
|
||||||
|
|
||||||
|
m_hdrMetadataDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VkResult Presenter::recreateSwapChain() {
|
||||||
VkResult vr;
|
VkResult vr;
|
||||||
|
|
||||||
if (m_swapchain)
|
if (m_swapchain)
|
||||||
destroySwapchain();
|
destroySwapchain();
|
||||||
|
|
||||||
if (m_surface) {
|
if (m_surface) {
|
||||||
vr = createSwapChain(desc);
|
vr = createSwapChain();
|
||||||
|
|
||||||
if (vr == VK_ERROR_SURFACE_LOST_KHR)
|
if (vr == VK_ERROR_SURFACE_LOST_KHR)
|
||||||
destroySurface();
|
destroySurface();
|
||||||
|
@ -189,14 +304,27 @@ namespace dxvk {
|
||||||
vr = createSurface();
|
vr = createSurface();
|
||||||
|
|
||||||
if (vr == VK_SUCCESS)
|
if (vr == VK_SUCCESS)
|
||||||
vr = createSwapChain(desc);
|
vr = createSwapChain();
|
||||||
}
|
}
|
||||||
|
|
||||||
return vr;
|
return vr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
VkResult Presenter::createSwapChain(const PresenterDesc& desc) {
|
void Presenter::updateSwapChain() {
|
||||||
|
if (m_dirtySurface || m_dirtySwapchain) {
|
||||||
|
destroySwapchain();
|
||||||
|
m_dirtySwapchain = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_dirtySurface) {
|
||||||
|
destroySurface();
|
||||||
|
m_dirtySurface = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VkResult Presenter::createSwapChain() {
|
||||||
VkSurfaceFullScreenExclusiveInfoEXT fullScreenExclusiveInfo = { VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT };
|
VkSurfaceFullScreenExclusiveInfoEXT fullScreenExclusiveInfo = { VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT };
|
||||||
fullScreenExclusiveInfo.fullScreenExclusive = m_fullscreenMode;
|
fullScreenExclusiveInfo.fullScreenExclusive = m_fullscreenMode;
|
||||||
|
|
||||||
|
@ -229,25 +357,22 @@ namespace dxvk {
|
||||||
|
|
||||||
// Select image extent based on current surface capabilities, and return
|
// Select image extent based on current surface capabilities, and return
|
||||||
// immediately if we cannot create an actual swap chain.
|
// immediately if we cannot create an actual swap chain.
|
||||||
m_info.imageExtent = pickImageExtent(caps.surfaceCapabilities, desc.imageExtent);
|
VkExtent2D imageExtent = pickImageExtent(caps.surfaceCapabilities, m_preferredExtent);
|
||||||
|
|
||||||
if (!m_info.imageExtent.width || !m_info.imageExtent.height) {
|
if (!imageExtent.width || !imageExtent.height)
|
||||||
m_info.imageCount = 0;
|
return VK_NOT_READY;
|
||||||
m_info.format = { VK_FORMAT_UNDEFINED, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
|
|
||||||
return VK_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select format based on swap chain properties
|
// Select format based on swap chain properties
|
||||||
if ((status = getSupportedFormats(formats)))
|
if ((status = getSupportedFormats(formats)))
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
m_info.format = pickFormat(formats.size(), formats.data(), desc.numFormats, desc.formats);
|
VkSurfaceFormatKHR surfaceFormat = pickSurfaceFormat(formats.size(), formats.data(), m_preferredFormat);
|
||||||
|
|
||||||
// Select a present mode for the current sync interval
|
// Select a present mode for the current sync interval
|
||||||
if ((status = getSupportedPresentModes(modes)))
|
if ((status = getSupportedPresentModes(modes)))
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
m_info.presentMode = pickPresentMode(modes.size(), modes.data(), m_info.syncInterval);
|
m_presentMode = pickPresentMode(modes.size(), modes.data(), m_preferredSyncInterval);
|
||||||
|
|
||||||
// Check whether we can change present modes dynamically. This may
|
// Check whether we can change present modes dynamically. This may
|
||||||
// influence the image count as well as further swap chain creation.
|
// influence the image count as well as further swap chain creation.
|
||||||
|
@ -268,7 +393,7 @@ namespace dxvk {
|
||||||
|
|
||||||
VkSurfacePresentModeEXT presentModeInfo = { VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT };
|
VkSurfacePresentModeEXT presentModeInfo = { VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT };
|
||||||
presentModeInfo.pNext = const_cast<void*>(std::exchange(surfaceInfo.pNext, &presentModeInfo));
|
presentModeInfo.pNext = const_cast<void*>(std::exchange(surfaceInfo.pNext, &presentModeInfo));
|
||||||
presentModeInfo.presentMode = m_info.presentMode;
|
presentModeInfo.presentMode = m_presentMode;
|
||||||
|
|
||||||
caps.pNext = &compatibleModeInfo;
|
caps.pNext = &compatibleModeInfo;
|
||||||
|
|
||||||
|
@ -324,8 +449,6 @@ namespace dxvk {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute swap chain image count based on available info
|
// 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 = m_fullscreenMode;
|
fullScreenInfo.fullScreenExclusive = m_fullscreenMode;
|
||||||
|
|
||||||
|
@ -335,17 +458,17 @@ namespace dxvk {
|
||||||
|
|
||||||
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 = pickImageCount(minImageCount, maxImageCount);
|
||||||
swapInfo.imageFormat = m_info.format.format;
|
swapInfo.imageFormat = surfaceFormat.format;
|
||||||
swapInfo.imageColorSpace = m_info.format.colorSpace;
|
swapInfo.imageColorSpace = surfaceFormat.colorSpace;
|
||||||
swapInfo.imageExtent = m_info.imageExtent;
|
swapInfo.imageExtent = imageExtent;
|
||||||
swapInfo.imageArrayLayers = 1;
|
swapInfo.imageArrayLayers = 1;
|
||||||
swapInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
|
swapInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
|
||||||
| VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
| VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||||
swapInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
swapInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
swapInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
swapInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||||
swapInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
swapInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||||
swapInfo.presentMode = m_info.presentMode;
|
swapInfo.presentMode = m_presentMode;
|
||||||
swapInfo.clipped = VK_TRUE;
|
swapInfo.clipped = VK_TRUE;
|
||||||
|
|
||||||
if (m_device->features().extFullScreenExclusive)
|
if (m_device->features().extFullScreenExclusive)
|
||||||
|
@ -356,26 +479,23 @@ namespace dxvk {
|
||||||
|
|
||||||
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: ", swapInfo.imageFormat,
|
||||||
"\n Color space: ", m_info.format.colorSpace,
|
"\n Color space: ", swapInfo.imageColorSpace,
|
||||||
"\n Present mode: ", m_info.presentMode, " (dynamic: ", (dynamicModes.empty() ? "no)" : "yes)"),
|
"\n Present mode: ", swapInfo.presentMode, " (dynamic: ", (dynamicModes.empty() ? "no)" : "yes)"),
|
||||||
"\n Buffer size: ", m_info.imageExtent.width, "x", m_info.imageExtent.height,
|
"\n Buffer size: ", swapInfo.imageExtent.width, "x", swapInfo.imageExtent.height,
|
||||||
"\n Image count: ", m_info.imageCount));
|
"\n Image count: ", swapInfo.minImageCount));
|
||||||
|
|
||||||
if ((status = m_vkd->vkCreateSwapchainKHR(m_vkd->device(),
|
if ((status = m_vkd->vkCreateSwapchainKHR(m_vkd->device(),
|
||||||
&swapInfo, nullptr, &m_swapchain)))
|
&swapInfo, nullptr, &m_swapchain)))
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
// Acquire images and create views
|
// Import actual swap chain images
|
||||||
std::vector<VkImage> images;
|
std::vector<VkImage> images;
|
||||||
|
|
||||||
if ((status = getSwapImages(images)))
|
if ((status = getSwapImages(images)))
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
// Update actual image count
|
for (uint32_t i = 0; i < images.size(); i++) {
|
||||||
m_info.imageCount = images.size();
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < m_info.imageCount; i++) {
|
|
||||||
std::string debugName = str::format("Vulkan swap image ", i);
|
std::string debugName = str::format("Vulkan swap image ", i);
|
||||||
|
|
||||||
DxvkImageCreateInfo imageInfo = { };
|
DxvkImageCreateInfo imageInfo = { };
|
||||||
|
@ -397,7 +517,7 @@ namespace dxvk {
|
||||||
|
|
||||||
// Create one set of semaphores per swap image, as well as a fence
|
// Create one set of semaphores per swap image, as well as a fence
|
||||||
// that we use to ensure that semaphores are safe to access.
|
// that we use to ensure that semaphores are safe to access.
|
||||||
uint32_t semaphoreCount = m_info.imageCount;
|
uint32_t semaphoreCount = images.size();
|
||||||
|
|
||||||
if (!m_device->features().extSwapchainMaintenance1.swapchainMaintenance1) {
|
if (!m_device->features().extSwapchainMaintenance1.swapchainMaintenance1) {
|
||||||
// Without support for present fences, just give up and allocate extra
|
// Without support for present fences, just give up and allocate extra
|
||||||
|
@ -437,61 +557,6 @@ namespace dxvk {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Presenter::supportsColorSpace(VkColorSpaceKHR colorspace) {
|
|
||||||
if (!m_surface)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::vector<VkSurfaceFormatKHR> surfaceFormats;
|
|
||||||
getSupportedFormats(surfaceFormats);
|
|
||||||
|
|
||||||
for (const auto& surfaceFormat : surfaceFormats) {
|
|
||||||
if (surfaceFormat.colorSpace == colorspace)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
return VK_SUCCESS;
|
|
||||||
|
|
||||||
m_info.syncInterval = syncInterval;
|
|
||||||
|
|
||||||
if (syncInterval >= m_dynamicModes.size())
|
|
||||||
return VK_ERROR_OUT_OF_DATE_KHR;
|
|
||||||
|
|
||||||
m_info.presentMode = m_dynamicModes[syncInterval];
|
|
||||||
return VK_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Presenter::setFrameRateLimit(double frameRate, uint32_t maxLatency) {
|
|
||||||
m_fpsLimiter.setTargetFrameRate(frameRate, maxLatency);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Presenter::setHdrMetadata(const VkHdrMetadataEXT& hdrMetadata) {
|
|
||||||
if (m_hdrMetadata->sType != VK_STRUCTURE_TYPE_HDR_METADATA_EXT) {
|
|
||||||
m_hdrMetadata = std::nullopt;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hdrMetadata.pNext)
|
|
||||||
Logger::warn("HDR metadata extensions not currently supported.");
|
|
||||||
|
|
||||||
m_hdrMetadata = hdrMetadata;
|
|
||||||
m_hdrMetadata->pNext = nullptr;
|
|
||||||
|
|
||||||
m_hdrMetadataDirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
VkResult Presenter::getSupportedFormats(std::vector<VkSurfaceFormatKHR>& formats) const {
|
VkResult Presenter::getSupportedFormats(std::vector<VkSurfaceFormatKHR>& formats) const {
|
||||||
uint32_t numFormats = 0;
|
uint32_t numFormats = 0;
|
||||||
|
|
||||||
|
@ -586,42 +651,148 @@ namespace dxvk {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
VkSurfaceFormatKHR Presenter::pickFormat(
|
VkSurfaceFormatKHR Presenter::pickSurfaceFormat(
|
||||||
uint32_t numSupported,
|
uint32_t numSupported,
|
||||||
const VkSurfaceFormatKHR* pSupported,
|
const VkSurfaceFormatKHR* pSupported,
|
||||||
uint32_t numDesired,
|
const VkSurfaceFormatKHR& desired) {
|
||||||
const VkSurfaceFormatKHR* pDesired) {
|
VkSurfaceFormatKHR result = { };
|
||||||
if (numDesired > 0) {
|
result.colorSpace = pickColorSpace(numSupported, pSupported, desired.colorSpace);
|
||||||
// If the implementation allows us to freely choose
|
result.format = pickFormat(numSupported, pSupported, result.colorSpace, desired.format);
|
||||||
// the format, we'll just use the preferred format.
|
return result;
|
||||||
if (numSupported == 1 && pSupported[0].format == VK_FORMAT_UNDEFINED)
|
}
|
||||||
return pDesired[0];
|
|
||||||
|
|
||||||
// If the preferred format is explicitly listed in
|
|
||||||
// the array of supported surface formats, use it
|
|
||||||
for (uint32_t i = 0; i < numDesired; i++) {
|
|
||||||
for (uint32_t j = 0; j < numSupported; j++) {
|
|
||||||
if (pSupported[j].format == pDesired[i].format
|
|
||||||
&& pSupported[j].colorSpace == pDesired[i].colorSpace)
|
|
||||||
return pSupported[j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If that didn't work, we'll fall back to a format
|
|
||||||
// which has similar properties to the preferred one
|
|
||||||
DxvkFormatFlags prefFlags = lookupFormatInfo(pDesired[0].format)->flags;
|
|
||||||
|
|
||||||
for (uint32_t j = 0; j < numSupported; j++) {
|
VkColorSpaceKHR Presenter::pickColorSpace(
|
||||||
auto currFlags = lookupFormatInfo(pSupported[j].format)->flags;
|
uint32_t numSupported,
|
||||||
|
const VkSurfaceFormatKHR* pSupported,
|
||||||
|
VkColorSpaceKHR desired) {
|
||||||
|
static const std::array<std::pair<VkColorSpaceKHR, VkColorSpaceKHR>, 2> fallbacks = {{
|
||||||
|
{ VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT, VK_COLOR_SPACE_HDR10_ST2084_EXT },
|
||||||
|
|
||||||
if ((currFlags & DxvkFormatFlag::ColorSpaceSrgb)
|
{ VK_COLOR_SPACE_HDR10_ST2084_EXT, VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT },
|
||||||
== (prefFlags & DxvkFormatFlag::ColorSpaceSrgb))
|
}};
|
||||||
return pSupported[j];
|
|
||||||
|
for (uint32_t i = 0; i < numSupported; i++) {
|
||||||
|
if (pSupported[i].colorSpace == desired)
|
||||||
|
return desired;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& f : fallbacks) {
|
||||||
|
if (f.first != desired)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < numSupported; i++) {
|
||||||
|
if (pSupported[i].colorSpace == f.second)
|
||||||
|
return f.second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, fall back to the first supported format
|
Logger::warn(str::format("No fallback color space found for ", desired, ", using ", pSupported[0].colorSpace));
|
||||||
return pSupported[0];
|
return pSupported[0].colorSpace;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VkFormat Presenter::pickFormat(
|
||||||
|
uint32_t numSupported,
|
||||||
|
const VkSurfaceFormatKHR* pSupported,
|
||||||
|
VkColorSpaceKHR colorSpace,
|
||||||
|
VkFormat format) {
|
||||||
|
static const std::array<VkFormat, 15> srgbFormatList = {
|
||||||
|
VK_FORMAT_B5G5R5A1_UNORM_PACK16,
|
||||||
|
VK_FORMAT_R5G5B5A1_UNORM_PACK16,
|
||||||
|
VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR,
|
||||||
|
VK_FORMAT_R5G6B5_UNORM_PACK16,
|
||||||
|
VK_FORMAT_B5G6R5_UNORM_PACK16,
|
||||||
|
VK_FORMAT_R8G8B8A8_SRGB,
|
||||||
|
VK_FORMAT_B8G8R8A8_SRGB,
|
||||||
|
VK_FORMAT_A8B8G8R8_SRGB_PACK32,
|
||||||
|
VK_FORMAT_R8G8B8A8_UNORM,
|
||||||
|
VK_FORMAT_B8G8R8A8_UNORM,
|
||||||
|
VK_FORMAT_A8B8G8R8_UNORM_PACK32,
|
||||||
|
VK_FORMAT_A2R10G10B10_UNORM_PACK32,
|
||||||
|
VK_FORMAT_A2B10G10R10_UNORM_PACK32,
|
||||||
|
VK_FORMAT_R16G16B16A16_UNORM,
|
||||||
|
VK_FORMAT_R16G16B16A16_SFLOAT,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::array<VkFormat, 5> hdr10FormatList = {
|
||||||
|
VK_FORMAT_A2R10G10B10_UNORM_PACK32,
|
||||||
|
VK_FORMAT_A2B10G10R10_UNORM_PACK32,
|
||||||
|
VK_FORMAT_R16G16B16A16_UNORM,
|
||||||
|
VK_FORMAT_R16G16B16A16_SFLOAT,
|
||||||
|
VK_FORMAT_E5B9G9R9_UFLOAT_PACK32,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::array<VkFormat, 1> scRGBFormatList = {
|
||||||
|
VK_FORMAT_R16G16B16A16_SFLOAT,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::array<PresenterFormatList, 3> compatLists = {{
|
||||||
|
{ VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
|
||||||
|
srgbFormatList.size(), srgbFormatList.data() },
|
||||||
|
{ VK_COLOR_SPACE_HDR10_ST2084_EXT,
|
||||||
|
hdr10FormatList.size(), hdr10FormatList.data() },
|
||||||
|
{ VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT,
|
||||||
|
scRGBFormatList.size(), scRGBFormatList.data() },
|
||||||
|
}};
|
||||||
|
|
||||||
|
// If the desired format is supported natively, use it
|
||||||
|
VkFormat fallback = VK_FORMAT_UNDEFINED;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < numSupported; i++) {
|
||||||
|
if (pSupported[i].colorSpace == colorSpace) {
|
||||||
|
if (pSupported[i].format == format)
|
||||||
|
return pSupported[i].format;
|
||||||
|
|
||||||
|
if (!fallback)
|
||||||
|
fallback = pSupported[i].format;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, find a supported format for the color space
|
||||||
|
const PresenterFormatList* compatList = nullptr;
|
||||||
|
|
||||||
|
for (const auto& l : compatLists) {
|
||||||
|
if (l.colorSpace == colorSpace)
|
||||||
|
compatList = &l;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!compatList)
|
||||||
|
return fallback;
|
||||||
|
|
||||||
|
// If the desired format is linear, ignore sRGB formats. We can do
|
||||||
|
// this because sRGB and linear formats must be supported in pairs.
|
||||||
|
// sRGB to linear fallbacks need to be allowed though in order to
|
||||||
|
// be able to select a format with a higher bit depth than requested.
|
||||||
|
bool desiredIsSrgb = lookupFormatInfo(format)->flags.test(DxvkFormatFlag::ColorSpaceSrgb);
|
||||||
|
bool desiredFound = false;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < compatList->formatCount; i++) {
|
||||||
|
bool formatIsSrgb = lookupFormatInfo(compatList->formats[i])->flags.test(DxvkFormatFlag::ColorSpaceSrgb);
|
||||||
|
|
||||||
|
if (!desiredIsSrgb && formatIsSrgb)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool isSupported = false;
|
||||||
|
|
||||||
|
if (compatList->formats[i] == format)
|
||||||
|
desiredFound = true;
|
||||||
|
|
||||||
|
for (uint32_t j = 0; j < numSupported && !isSupported; j++)
|
||||||
|
isSupported = pSupported[j].colorSpace == colorSpace && pSupported[j].format == compatList->formats[i];
|
||||||
|
|
||||||
|
if (isSupported) {
|
||||||
|
fallback = compatList->formats[i];
|
||||||
|
|
||||||
|
if (desiredFound)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!desiredFound)
|
||||||
|
Logger::warn(str::format("Desired format ", format, " not in compatibility list for ", colorSpace, ", using ", fallback));
|
||||||
|
|
||||||
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -671,16 +842,12 @@ namespace dxvk {
|
||||||
|
|
||||||
uint32_t Presenter::pickImageCount(
|
uint32_t Presenter::pickImageCount(
|
||||||
uint32_t minImageCount,
|
uint32_t minImageCount,
|
||||||
uint32_t maxImageCount,
|
uint32_t maxImageCount) {
|
||||||
uint32_t desired) {
|
|
||||||
uint32_t count = minImageCount + 1;
|
uint32_t count = minImageCount + 1;
|
||||||
|
|
||||||
if (count < desired)
|
|
||||||
count = desired;
|
|
||||||
|
|
||||||
if (count > maxImageCount && maxImageCount != 0)
|
if (count > maxImageCount && maxImageCount != 0)
|
||||||
count = maxImageCount;
|
count = maxImageCount;
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,25 +33,7 @@ namespace dxvk {
|
||||||
* an input during swap chain creation.
|
* an input during swap chain creation.
|
||||||
*/
|
*/
|
||||||
struct PresenterDesc {
|
struct PresenterDesc {
|
||||||
VkExtent2D imageExtent = { };
|
bool deferSurfaceCreation = false;
|
||||||
uint32_t imageCount = 0u;
|
|
||||||
uint32_t numFormats = 0u;
|
|
||||||
VkSurfaceFormatKHR formats[4] = { };
|
|
||||||
bool deferSurfaceCreation = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Presenter properties
|
|
||||||
*
|
|
||||||
* Contains the actual properties
|
|
||||||
* of the underlying swap chain.
|
|
||||||
*/
|
|
||||||
struct PresenterInfo {
|
|
||||||
VkSurfaceFormatKHR format;
|
|
||||||
VkPresentModeKHR presentMode;
|
|
||||||
VkExtent2D imageExtent;
|
|
||||||
uint32_t imageCount;
|
|
||||||
uint32_t syncInterval;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -78,6 +60,15 @@ namespace dxvk {
|
||||||
VkResult result = VK_NOT_READY;
|
VkResult result = VK_NOT_READY;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Format compatibility list
|
||||||
|
*/
|
||||||
|
struct PresenterFormatList {
|
||||||
|
VkColorSpaceKHR colorSpace;
|
||||||
|
size_t formatCount;
|
||||||
|
const VkFormat* formats;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Vulkan presenter
|
* \brief Vulkan presenter
|
||||||
*
|
*
|
||||||
|
@ -98,21 +89,29 @@ namespace dxvk {
|
||||||
~Presenter();
|
~Presenter();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Actual presenter info
|
* \brief Tests swap chain status
|
||||||
* \returns Swap chain properties
|
*
|
||||||
|
* If no swapchain currently exists, this method may create
|
||||||
|
* one so that presentation can subsequently be performed.
|
||||||
|
* \returns One of the following return codes:
|
||||||
|
* - \c VK_SUCCESS if a valid swapchain exists
|
||||||
|
* - \c VK_NOT_READY if no swap chain can be created
|
||||||
|
* - Any other error code if swap chain creation failed.
|
||||||
*/
|
*/
|
||||||
PresenterInfo info() const;
|
VkResult checkSwapChainStatus();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Acquires next image
|
* \brief Acquires next image
|
||||||
*
|
*
|
||||||
* Potentially blocks the calling thread.
|
* Tries to acquire an image from the underlying Vulkan
|
||||||
* If this returns an error, the swap chain
|
* swapchain. May recreate the swapchain if any surface
|
||||||
* must be recreated and a new image must
|
* properties or user-specified parameters have changed.
|
||||||
* be acquired before proceeding.
|
* Potentially blocks the calling thread, and must not be
|
||||||
|
* called if any present call is currently in flight.
|
||||||
* \param [out] sync Synchronization semaphores
|
* \param [out] sync Synchronization semaphores
|
||||||
* \param [out] image Acquired swap chain image
|
* \param [out] image Acquired swap chain image
|
||||||
* \returns Status of the operation
|
* \returns Status of the operation. May return
|
||||||
|
* \c VK_NOT_READY if no swap chain exists.
|
||||||
*/
|
*/
|
||||||
VkResult acquireNextImage(
|
VkResult acquireNextImage(
|
||||||
PresenterSync& sync,
|
PresenterSync& sync,
|
||||||
|
@ -121,17 +120,12 @@ namespace dxvk {
|
||||||
/**
|
/**
|
||||||
* \brief Presents current image
|
* \brief Presents current image
|
||||||
*
|
*
|
||||||
* Presents the current image. If this returns
|
* Presents the last successfuly acquired image.
|
||||||
* an error, the swap chain must be recreated,
|
|
||||||
* but do not present before acquiring an image.
|
|
||||||
* \param [in] mode Present mode
|
|
||||||
* \param [in] frameId Frame number.
|
* \param [in] frameId Frame number.
|
||||||
* Must increase monotonically.
|
* Must increase monotonically.
|
||||||
* \returns Status of the operation
|
* \returns Status of the operation
|
||||||
*/
|
*/
|
||||||
VkResult presentImage(
|
VkResult presentImage(uint64_t frameId);
|
||||||
VkPresentModeKHR mode,
|
|
||||||
uint64_t frameId);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Signals a given frame
|
* \brief Signals a given frame
|
||||||
|
@ -141,34 +135,17 @@ namespace dxvk {
|
||||||
* called before GPU work prior to the present submission has
|
* called before GPU work prior to the present submission has
|
||||||
* completed in order to maintain consistency.
|
* completed in order to maintain consistency.
|
||||||
* \param [in] result Presentation result
|
* \param [in] result Presentation result
|
||||||
* \param [in] mode Present mode
|
|
||||||
* \param [in] frameId Frame number
|
* \param [in] frameId Frame number
|
||||||
*/
|
*/
|
||||||
void signalFrame(
|
void signalFrame(VkResult result, uint64_t frameId);
|
||||||
VkResult result,
|
|
||||||
VkPresentModeKHR mode,
|
|
||||||
uint64_t frameId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Changes presenter properties
|
|
||||||
*
|
|
||||||
* Recreates the swap chain immediately. Note that
|
|
||||||
* no swap chain resources must be in use by the
|
|
||||||
* GPU at the time this is called.
|
|
||||||
* \param [in] desc Swap chain description
|
|
||||||
* \param [in] surface New Vulkan surface
|
|
||||||
*/
|
|
||||||
VkResult recreateSwapChain(
|
|
||||||
const PresenterDesc& desc);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Changes sync interval
|
* \brief Changes sync interval
|
||||||
*
|
*
|
||||||
* If this returns an error, the swap chain must
|
* Changes the Vulkan present mode as necessary.
|
||||||
* be recreated.
|
|
||||||
* \param [in] syncInterval New sync interval
|
* \param [in] syncInterval New sync interval
|
||||||
*/
|
*/
|
||||||
VkResult setSyncInterval(uint32_t syncInterval);
|
void setSyncInterval(uint32_t syncInterval);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Changes maximum frame rate
|
* \brief Changes maximum frame rate
|
||||||
|
@ -179,16 +156,32 @@ namespace dxvk {
|
||||||
void setFrameRateLimit(double frameRate, uint32_t maxLatency);
|
void setFrameRateLimit(double frameRate, uint32_t maxLatency);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Checks whether a Vulkan swap chain exists
|
* \brief Sets preferred color space and format
|
||||||
*
|
*
|
||||||
* On Windows, there are situations where we cannot create
|
* If the Vulkan surface does not natively support the given
|
||||||
* a swap chain as the surface size can reach zero, and no
|
* parameter combo, it will try to select a format and color
|
||||||
* presentation can be performed.
|
* space with similar properties.
|
||||||
* \returns \c true if the presenter has a swap chain.
|
* \param [in] format Preferred surface format
|
||||||
*/
|
*/
|
||||||
bool hasSwapChain() const {
|
void setSurfaceFormat(VkSurfaceFormatKHR format);
|
||||||
return m_swapchain;
|
|
||||||
}
|
/**
|
||||||
|
* \brief Sets preferred surface extent
|
||||||
|
*
|
||||||
|
* The preferred surface extent is only relevant if the Vulkan
|
||||||
|
* surface itself does not have a fixed size. Should match the
|
||||||
|
* back buffer size of the application.
|
||||||
|
* \param [in] extent Preferred surface extent
|
||||||
|
*/
|
||||||
|
void setSurfaceExtent(VkExtent2D extent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Sets HDR metadata
|
||||||
|
*
|
||||||
|
* Updated HDR metadata will be applied on the next \c acquire.
|
||||||
|
* \param [in] hdrMetadata HDR Metadata
|
||||||
|
*/
|
||||||
|
void setHdrMetadata(VkHdrMetadataEXT hdrMetadata);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Checks support for a Vulkan color space
|
* \brief Checks support for a Vulkan color space
|
||||||
|
@ -199,12 +192,13 @@ namespace dxvk {
|
||||||
bool supportsColorSpace(VkColorSpaceKHR colorspace);
|
bool supportsColorSpace(VkColorSpaceKHR colorspace);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Sets HDR metadata
|
* \brief Invalidates Vulkan surface
|
||||||
*
|
*
|
||||||
* Updated HDR metadata will be applied on the next \c acquire.
|
* This will cause the Vulkan surface to be destroyed and
|
||||||
* \param [in] hdrMetadata HDR Metadata
|
* recreated on the next \c acquire call. This is a hacky
|
||||||
|
* workaround to support windows with multiple surfaces.
|
||||||
*/
|
*/
|
||||||
void setHdrMetadata(const VkHdrMetadataEXT& hdrMetadata);
|
void invalidateSurface();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -214,7 +208,7 @@ namespace dxvk {
|
||||||
Rc<vk::InstanceFn> m_vki;
|
Rc<vk::InstanceFn> m_vki;
|
||||||
Rc<vk::DeviceFn> m_vkd;
|
Rc<vk::DeviceFn> m_vkd;
|
||||||
|
|
||||||
PresenterInfo m_info = { };
|
dxvk::mutex m_surfaceMutex;
|
||||||
PresenterSurfaceProc m_surfaceProc;
|
PresenterSurfaceProc m_surfaceProc;
|
||||||
|
|
||||||
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
|
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
|
||||||
|
@ -227,6 +221,15 @@ namespace dxvk {
|
||||||
|
|
||||||
std::vector<VkPresentModeKHR> m_dynamicModes;
|
std::vector<VkPresentModeKHR> m_dynamicModes;
|
||||||
|
|
||||||
|
VkExtent2D m_preferredExtent = { };
|
||||||
|
VkSurfaceFormatKHR m_preferredFormat = { };
|
||||||
|
uint32_t m_preferredSyncInterval = 1u;
|
||||||
|
|
||||||
|
bool m_dirtySwapchain = false;
|
||||||
|
bool m_dirtySurface = false;
|
||||||
|
|
||||||
|
VkPresentModeKHR m_presentMode = VK_PRESENT_MODE_FIFO_KHR;
|
||||||
|
|
||||||
uint32_t m_imageIndex = 0;
|
uint32_t m_imageIndex = 0;
|
||||||
uint32_t m_frameIndex = 0;
|
uint32_t m_frameIndex = 0;
|
||||||
|
|
||||||
|
@ -246,8 +249,11 @@ namespace dxvk {
|
||||||
alignas(CACHE_LINE_SIZE)
|
alignas(CACHE_LINE_SIZE)
|
||||||
FpsLimiter m_fpsLimiter;
|
FpsLimiter m_fpsLimiter;
|
||||||
|
|
||||||
VkResult createSwapChain(
|
void updateSwapChain();
|
||||||
const PresenterDesc& desc);
|
|
||||||
|
VkResult recreateSwapChain();
|
||||||
|
|
||||||
|
VkResult createSwapChain();
|
||||||
|
|
||||||
VkResult getSupportedFormats(
|
VkResult getSupportedFormats(
|
||||||
std::vector<VkSurfaceFormatKHR>& formats) const;
|
std::vector<VkSurfaceFormatKHR>& formats) const;
|
||||||
|
@ -258,11 +264,21 @@ namespace dxvk {
|
||||||
VkResult getSwapImages(
|
VkResult getSwapImages(
|
||||||
std::vector<VkImage>& images);
|
std::vector<VkImage>& images);
|
||||||
|
|
||||||
VkSurfaceFormatKHR pickFormat(
|
VkSurfaceFormatKHR pickSurfaceFormat(
|
||||||
uint32_t numSupported,
|
uint32_t numSupported,
|
||||||
const VkSurfaceFormatKHR* pSupported,
|
const VkSurfaceFormatKHR* pSupported,
|
||||||
uint32_t numDesired,
|
const VkSurfaceFormatKHR& desired);
|
||||||
const VkSurfaceFormatKHR* pDesired);
|
|
||||||
|
VkColorSpaceKHR pickColorSpace(
|
||||||
|
uint32_t numSupported,
|
||||||
|
const VkSurfaceFormatKHR* pSupported,
|
||||||
|
VkColorSpaceKHR desired);
|
||||||
|
|
||||||
|
VkFormat pickFormat(
|
||||||
|
uint32_t numSupported,
|
||||||
|
const VkSurfaceFormatKHR* pSupported,
|
||||||
|
VkColorSpaceKHR colorSpace,
|
||||||
|
VkFormat format);
|
||||||
|
|
||||||
VkPresentModeKHR pickPresentMode(
|
VkPresentModeKHR pickPresentMode(
|
||||||
uint32_t numSupported,
|
uint32_t numSupported,
|
||||||
|
@ -275,8 +291,7 @@ namespace dxvk {
|
||||||
|
|
||||||
uint32_t pickImageCount(
|
uint32_t pickImageCount(
|
||||||
uint32_t minImageCount,
|
uint32_t minImageCount,
|
||||||
uint32_t maxImageCount,
|
uint32_t maxImageCount);
|
||||||
uint32_t desired);
|
|
||||||
|
|
||||||
VkResult createSurface();
|
VkResult createSurface();
|
||||||
|
|
||||||
|
|
|
@ -145,7 +145,7 @@ namespace dxvk {
|
||||||
entry.result = entry.submit.cmdList->submit(m_semaphores, m_timelines);
|
entry.result = entry.submit.cmdList->submit(m_semaphores, m_timelines);
|
||||||
entry.timelines = m_timelines;
|
entry.timelines = m_timelines;
|
||||||
} else if (entry.present.presenter != nullptr) {
|
} else if (entry.present.presenter != nullptr) {
|
||||||
entry.result = entry.present.presenter->presentImage(entry.present.presentMode, entry.present.frameId);
|
entry.result = entry.present.presenter->presentImage(entry.present.frameId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_callback)
|
if (m_callback)
|
||||||
|
@ -235,8 +235,7 @@ namespace dxvk {
|
||||||
// Signal the frame and then immediately destroy the reference.
|
// Signal the frame and then immediately destroy the reference.
|
||||||
// This is necessary since the front-end may want to explicitly
|
// This is necessary since the front-end may want to explicitly
|
||||||
// destroy the presenter object.
|
// destroy the presenter object.
|
||||||
entry.present.presenter->signalFrame(entry.result,
|
entry.present.presenter->signalFrame(entry.result, entry.present.frameId);
|
||||||
entry.present.presentMode, entry.present.frameId);
|
|
||||||
entry.present.presenter = nullptr;
|
entry.present.presenter = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,6 @@ namespace dxvk {
|
||||||
*/
|
*/
|
||||||
struct DxvkPresentInfo {
|
struct DxvkPresentInfo {
|
||||||
Rc<Presenter> presenter;
|
Rc<Presenter> presenter;
|
||||||
VkPresentModeKHR presentMode;
|
|
||||||
uint64_t frameId;
|
uint64_t frameId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue