mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-03-01 19:29:16 +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
06b44c6237
commit
43838d3df8
@ -174,11 +174,11 @@ namespace dxvk {
|
||||
const DXGI_SWAP_CHAIN_DESC1* pDesc,
|
||||
const UINT* pNodeMasks,
|
||||
IUnknown* const* ppPresentQueues) {
|
||||
m_dirty |= m_desc.Format != pDesc->Format
|
||||
|| m_desc.Width != pDesc->Width
|
||||
|| m_desc.Height != pDesc->Height
|
||||
|| m_desc.BufferCount != pDesc->BufferCount
|
||||
|| m_desc.Flags != pDesc->Flags;
|
||||
if (m_desc.Format != pDesc->Format)
|
||||
m_presenter->setSurfaceFormat(GetSurfaceFormat(pDesc->Format));
|
||||
|
||||
if (m_desc.Width != pDesc->Width || m_desc.Height != pDesc->Height)
|
||||
m_presenter->setSurfaceExtent({ m_desc.Width, m_desc.Height });
|
||||
|
||||
m_desc = *pDesc;
|
||||
CreateBackBuffers();
|
||||
@ -251,33 +251,32 @@ namespace dxvk {
|
||||
UINT SyncInterval,
|
||||
UINT PresentFlags,
|
||||
const DXGI_PRESENT_PARAMETERS* pPresentParameters) {
|
||||
if (!(PresentFlags & DXGI_PRESENT_TEST))
|
||||
m_dirty |= m_presenter->setSyncInterval(SyncInterval) != VK_SUCCESS;
|
||||
|
||||
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)
|
||||
hr = DXGI_ERROR_DEVICE_RESET;
|
||||
|
||||
if (PresentFlags & DXGI_PRESENT_TEST)
|
||||
return hr;
|
||||
if (PresentFlags & DXGI_PRESENT_TEST) {
|
||||
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) {
|
||||
SyncFrameLatency();
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (std::exchange(m_dirty, false))
|
||||
RecreateSwapChain();
|
||||
|
||||
try {
|
||||
hr = PresentImage(SyncInterval);
|
||||
} catch (const DxvkError& e) {
|
||||
@ -298,7 +297,8 @@ namespace dxvk {
|
||||
DXGI_COLOR_SPACE_TYPE ColorSpace) {
|
||||
UINT supportFlags = 0;
|
||||
|
||||
const VkColorSpaceKHR vkColorSpace = ConvertColorSpace(ColorSpace);
|
||||
VkColorSpaceKHR vkColorSpace = ConvertColorSpace(ColorSpace);
|
||||
|
||||
if (m_presenter->supportsColorSpace(vkColorSpace))
|
||||
supportFlags |= DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT;
|
||||
|
||||
@ -308,13 +308,14 @@ namespace dxvk {
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D11SwapChain::SetColorSpace(
|
||||
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;
|
||||
|
||||
const VkColorSpaceKHR vkColorSpace = ConvertColorSpace(ColorSpace);
|
||||
m_dirty |= vkColorSpace != m_colorspace;
|
||||
m_colorspace = vkColorSpace;
|
||||
m_colorSpace = colorSpace;
|
||||
|
||||
m_presenter->setSurfaceFormat(GetSurfaceFormat(m_desc.Format));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@ -378,27 +379,19 @@ namespace dxvk {
|
||||
|
||||
SynchronizePresent();
|
||||
|
||||
if (!m_presenter->hasSwapChain())
|
||||
return DXGI_STATUS_OCCLUDED;
|
||||
m_presenter->setSyncInterval(SyncInterval);
|
||||
|
||||
// Presentation semaphores and WSI swap chain image
|
||||
PresenterSync sync;
|
||||
|
||||
Rc<DxvkImage> backBuffer;
|
||||
|
||||
VkResult status = m_presenter->acquireNextImage(sync, backBuffer);
|
||||
|
||||
while (status != VK_SUCCESS) {
|
||||
RecreateSwapChain();
|
||||
if (status < 0)
|
||||
return E_FAIL;
|
||||
|
||||
if (!m_presenter->hasSwapChain())
|
||||
return DXGI_STATUS_OCCLUDED;
|
||||
|
||||
status = m_presenter->acquireNextImage(sync, backBuffer);
|
||||
|
||||
if (status == VK_SUBOPTIMAL_KHR)
|
||||
break;
|
||||
}
|
||||
if (status == VK_NOT_READY)
|
||||
return DXGI_STATUS_OCCLUDED;
|
||||
|
||||
m_frameId += 1;
|
||||
|
||||
@ -425,7 +418,7 @@ namespace dxvk {
|
||||
cSync = sync,
|
||||
cHud = m_hud,
|
||||
cPresenter = m_presenter,
|
||||
cColorSpace = m_colorspace,
|
||||
cColorSpace = m_colorSpace,
|
||||
cFrameId = m_frameId
|
||||
] (DxvkContext* ctx) {
|
||||
// Blit the D3D back buffer onto the actual Vulkan
|
||||
@ -448,7 +441,6 @@ namespace dxvk {
|
||||
ctx->flushCommandList(nullptr);
|
||||
|
||||
cDevice->presentImage(cPresenter,
|
||||
cPresenter->info().presentMode,
|
||||
cFrameId, cPresentStatus);
|
||||
});
|
||||
|
||||
@ -480,30 +472,7 @@ namespace dxvk {
|
||||
|
||||
|
||||
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->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() {
|
||||
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 presenterDesc = { };
|
||||
presenterDesc.deferSurfaceCreation = m_parent->GetOptions()->deferSurfaceCreation;
|
||||
|
||||
m_presenter = new Presenter(m_device, m_frameLatencySignal, presenterDesc, [
|
||||
@ -531,6 +497,8 @@ namespace dxvk {
|
||||
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());
|
||||
}
|
||||
|
||||
@ -658,46 +626,26 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
uint32_t D3D11SwapChain::PickFormats(
|
||||
DXGI_FORMAT Format,
|
||||
VkSurfaceFormatKHR* pDstFormats) {
|
||||
uint32_t n = 0;
|
||||
|
||||
VkSurfaceFormatKHR D3D11SwapChain::GetSurfaceFormat(DXGI_FORMAT Format) {
|
||||
switch (Format) {
|
||||
default:
|
||||
Logger::warn(str::format("D3D11SwapChain: Unexpected format: ", m_desc.Format));
|
||||
[[fallthrough]];
|
||||
|
||||
[[fallthrough]];
|
||||
|
||||
case DXGI_FORMAT_R8G8B8A8_UNORM:
|
||||
case DXGI_FORMAT_B8G8R8A8_UNORM: {
|
||||
pDstFormats[n++] = { VK_FORMAT_R8G8B8A8_UNORM, m_colorspace };
|
||||
pDstFormats[n++] = { VK_FORMAT_B8G8R8A8_UNORM, m_colorspace };
|
||||
} break;
|
||||
|
||||
case DXGI_FORMAT_B8G8R8A8_UNORM:
|
||||
return { VK_FORMAT_R8G8B8A8_UNORM, m_colorSpace };
|
||||
|
||||
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
|
||||
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: {
|
||||
pDstFormats[n++] = { VK_FORMAT_R8G8B8A8_SRGB, m_colorspace };
|
||||
pDstFormats[n++] = { VK_FORMAT_B8G8R8A8_SRGB, m_colorspace };
|
||||
} break;
|
||||
|
||||
case DXGI_FORMAT_R10G10B10A2_UNORM: {
|
||||
pDstFormats[n++] = { VK_FORMAT_A2B10G10R10_UNORM_PACK32, m_colorspace };
|
||||
pDstFormats[n++] = { VK_FORMAT_A2R10G10B10_UNORM_PACK32, m_colorspace };
|
||||
} break;
|
||||
|
||||
case DXGI_FORMAT_R16G16B16A16_FLOAT: {
|
||||
pDstFormats[n++] = { VK_FORMAT_R16G16B16A16_SFLOAT, m_colorspace };
|
||||
} break;
|
||||
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
|
||||
return { VK_FORMAT_R8G8B8A8_SRGB, m_colorSpace };
|
||||
|
||||
case DXGI_FORMAT_R10G10B10A2_UNORM:
|
||||
return { VK_FORMAT_A2B10G10R10_UNORM_PACK32, m_colorSpace };
|
||||
|
||||
case DXGI_FORMAT_R16G16B16A16_FLOAT:
|
||||
return { VK_FORMAT_R16G16B16A16_SFLOAT, m_colorSpace };
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
@ -136,8 +134,6 @@ namespace dxvk {
|
||||
|
||||
void SynchronizePresent();
|
||||
|
||||
void RecreateSwapChain();
|
||||
|
||||
void CreateFrameLatencyEvent();
|
||||
|
||||
void CreatePresenter();
|
||||
@ -154,12 +150,7 @@ namespace dxvk {
|
||||
|
||||
uint32_t GetActualFrameLatency();
|
||||
|
||||
uint32_t PickFormats(
|
||||
DXGI_FORMAT Format,
|
||||
VkSurfaceFormatKHR* pDstFormats);
|
||||
|
||||
uint32_t PickImageCount(
|
||||
UINT Preferred);
|
||||
VkSurfaceFormatKHR GetSurfaceFormat(DXGI_FORMAT Format);
|
||||
|
||||
std::string GetApiName() const;
|
||||
|
||||
|
@ -153,18 +153,16 @@ namespace dxvk {
|
||||
|
||||
UpdateWindowCtx();
|
||||
|
||||
bool recreate = false;
|
||||
recreate |= m_wctx->presenter == nullptr;
|
||||
bool recreate = !m_wctx->presenter;
|
||||
|
||||
if (options->deferSurfaceCreation)
|
||||
recreate |= m_parent->IsDeviceReset();
|
||||
|
||||
if (m_wctx->presenter != nullptr) {
|
||||
m_dirty |= m_wctx->presenter->setSyncInterval(presentInterval) != VK_SUCCESS;
|
||||
m_dirty |= !m_wctx->presenter->hasSwapChain();
|
||||
}
|
||||
if (m_wctx->presenter)
|
||||
m_wctx->presenter->setSyncInterval(presentInterval);
|
||||
|
||||
m_dirty |= UpdatePresentRegion(pSourceRect, pDestRect);
|
||||
m_dirty |= recreate;
|
||||
UpdatePresentRegion(pSourceRect, pDestRect);
|
||||
UpdatePresentParameters();
|
||||
|
||||
#ifdef _WIN32
|
||||
const bool useGDIFallback = m_partialCopy && !HasFrontBuffer();
|
||||
@ -174,17 +172,11 @@ namespace dxvk {
|
||||
|
||||
try {
|
||||
if (recreate)
|
||||
CreatePresenter();
|
||||
|
||||
if (std::exchange(m_dirty, false))
|
||||
RecreateSwapChain();
|
||||
RecreateSurface();
|
||||
|
||||
// We aren't going to device loss simply because
|
||||
// 99% of D3D9 games don't handle this properly and
|
||||
// just end up crashing (like with alt-tab loss)
|
||||
if (!m_wctx->presenter->hasSwapChain())
|
||||
return D3D_OK;
|
||||
|
||||
UpdateTargetFrameRate(presentInterval);
|
||||
PresentImage(presentInterval);
|
||||
return D3D_OK;
|
||||
@ -606,9 +598,6 @@ namespace dxvk {
|
||||
this->SynchronizePresent();
|
||||
this->NormalizePresentParameters(pPresentParams);
|
||||
|
||||
m_dirty |= m_presentParams.BackBufferFormat != pPresentParams->BackBufferFormat
|
||||
|| m_presentParams.BackBufferCount != pPresentParams->BackBufferCount;
|
||||
|
||||
bool changeFullscreen = m_presentParams.Windowed != pPresentParams->Windowed;
|
||||
|
||||
if (pPresentParams->Windowed) {
|
||||
@ -640,6 +629,8 @@ namespace dxvk {
|
||||
if (changeFullscreen)
|
||||
SetGammaRamp(0, &m_ramp);
|
||||
|
||||
UpdatePresentParameters();
|
||||
|
||||
hr = CreateBackBuffers(m_presentParams.BackBufferCount, m_presentParams.Flags);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
@ -826,27 +817,13 @@ namespace dxvk {
|
||||
SynchronizePresent();
|
||||
|
||||
// Presentation semaphores and WSI swap chain image
|
||||
PresenterInfo info = m_wctx->presenter->info();
|
||||
PresenterSync sync = { };
|
||||
|
||||
Rc<DxvkImage> backBuffer;
|
||||
|
||||
VkResult status = m_wctx->presenter->acquireNextImage(sync, backBuffer);
|
||||
|
||||
while (status != VK_SUCCESS) {
|
||||
RecreateSwapChain();
|
||||
|
||||
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;
|
||||
}
|
||||
if (status < 0 || status == VK_NOT_READY)
|
||||
break;
|
||||
|
||||
VkRect2D srcRect = {
|
||||
{ int32_t(m_srcRect.left), int32_t(m_srcRect.top) },
|
||||
@ -912,7 +889,6 @@ namespace dxvk {
|
||||
uint64_t frameId = cRepeat ? 0 : cFrameId;
|
||||
|
||||
cDevice->presentImage(cPresenter,
|
||||
cPresenter->info().presentMode,
|
||||
frameId, cPresentStatus);
|
||||
});
|
||||
|
||||
@ -931,29 +907,15 @@ namespace dxvk {
|
||||
|
||||
|
||||
void D3D9SwapChainEx::SynchronizePresent() {
|
||||
// Recreate swap chain if the previous present call failed
|
||||
VkResult status = m_device->waitForSubmission(&m_presentStatus);
|
||||
|
||||
if (status != VK_SUCCESS)
|
||||
RecreateSwapChain();
|
||||
m_device->waitForSubmission(&m_presentStatus);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
PresenterDesc presenterDesc;
|
||||
presenterDesc.imageExtent = GetPresentExtent();
|
||||
presenterDesc.imageCount = PickImageCount(m_presentParams.BackBufferCount + 1);
|
||||
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));
|
||||
void D3D9SwapChainEx::RecreateSurface() {
|
||||
if (m_wctx->presenter)
|
||||
m_wctx->presenter->invalidateSurface();
|
||||
else
|
||||
CreatePresenter();
|
||||
}
|
||||
|
||||
|
||||
@ -965,9 +927,6 @@ namespace dxvk {
|
||||
m_presentStatus.result = VK_SUCCESS;
|
||||
|
||||
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;
|
||||
|
||||
m_wctx->presenter = new Presenter(m_device,
|
||||
@ -982,6 +941,12 @@ namespace dxvk {
|
||||
vki->instance(),
|
||||
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(
|
||||
D3D9Format Format,
|
||||
VkSurfaceFormatKHR* pDstFormats) {
|
||||
uint32_t n = 0;
|
||||
VkSurfaceFormatKHR D3D9SwapChainEx::GetSurfaceFormat() {
|
||||
D3D9Format format = EnumerateFormat(m_presentParams.BackBufferFormat);
|
||||
|
||||
switch (Format) {
|
||||
switch (format) {
|
||||
default:
|
||||
Logger::warn(str::format("D3D9SwapChainEx: Unexpected format: ", Format));
|
||||
[[fallthrough]];
|
||||
Logger::warn(str::format("D3D9SwapChainEx: Unexpected format: ", format));
|
||||
[[fallthrough]];
|
||||
|
||||
case D3D9Format::A8R8G8B8:
|
||||
case D3D9Format::X8R8G8B8:
|
||||
return { VK_FORMAT_B8G8R8A8_UNORM, m_colorspace };
|
||||
|
||||
case D3D9Format::A8B8G8R8:
|
||||
case D3D9Format::X8B8G8R8: {
|
||||
pDstFormats[n++] = { VK_FORMAT_R8G8B8A8_UNORM, m_colorspace };
|
||||
pDstFormats[n++] = { VK_FORMAT_B8G8R8A8_UNORM, m_colorspace };
|
||||
} break;
|
||||
case D3D9Format::X8B8G8R8:
|
||||
return { VK_FORMAT_R8G8B8A8_UNORM, m_colorspace };
|
||||
|
||||
case D3D9Format::A2R10G10B10:
|
||||
case D3D9Format::A2B10G10R10: {
|
||||
pDstFormats[n++] = { VK_FORMAT_A2B10G10R10_UNORM_PACK32, m_colorspace };
|
||||
pDstFormats[n++] = { VK_FORMAT_A2R10G10B10_UNORM_PACK32, m_colorspace };
|
||||
} break;
|
||||
return { VK_FORMAT_A2R10G10B10_UNORM_PACK32, m_colorspace };
|
||||
|
||||
case D3D9Format::A2B10G10R10:
|
||||
return { VK_FORMAT_A2B10G10R10_UNORM_PACK32, m_colorspace };
|
||||
|
||||
case D3D9Format::X1R5G5B5:
|
||||
case D3D9Format::A1R5G5B5: {
|
||||
pDstFormats[n++] = { 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::A1R5G5B5:
|
||||
return { VK_FORMAT_B5G5R5A1_UNORM_PACK16, m_colorspace };
|
||||
|
||||
case D3D9Format::R5G6B5: {
|
||||
pDstFormats[n++] = { VK_FORMAT_B5G6R5_UNORM_PACK16, m_colorspace };
|
||||
pDstFormats[n++] = { VK_FORMAT_R5G6B5_UNORM_PACK16, m_colorspace };
|
||||
} break;
|
||||
case D3D9Format::R5G6B5:
|
||||
return { VK_FORMAT_B5G6R5_UNORM_PACK16, m_colorspace };
|
||||
|
||||
case D3D9Format::A16B16G16R16F: {
|
||||
if (m_unlockAdditionalFormats) {
|
||||
pDstFormats[n++] = { VK_FORMAT_R16G16B16A16_SFLOAT, m_colorspace };
|
||||
} else {
|
||||
Logger::warn(str::format("D3D9SwapChainEx: Unexpected format: ", Format));
|
||||
if (!m_unlockAdditionalFormats) {
|
||||
Logger::warn(str::format("D3D9SwapChainEx: Unexpected format: ", format));
|
||||
return VkSurfaceFormatKHR { };
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
bool D3D9SwapChainEx::UpdatePresentRegion(const RECT* pSourceRect, const RECT* pDestRect) {
|
||||
void D3D9SwapChainEx::UpdatePresentRegion(const RECT* pSourceRect, const RECT* pDestRect) {
|
||||
const bool isWindowed = m_presentParams.Windowed;
|
||||
|
||||
// Tests show that present regions are ignored in fullscreen
|
||||
@ -1333,15 +1282,15 @@ namespace dxvk {
|
||||
|| dstRect.right - dstRect.left != LONG(width)
|
||||
|| 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_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() {
|
||||
@ -1386,9 +1335,11 @@ namespace dxvk {
|
||||
if (!CheckColorSpaceSupport(ColorSpace))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
m_swapchain->m_dirty |= ColorSpace != m_swapchain->m_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;
|
||||
}
|
||||
|
||||
@ -1397,8 +1348,10 @@ namespace dxvk {
|
||||
if (!pHDRMetadata)
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
m_swapchain->m_hdrMetadata = *pHDRMetadata;
|
||||
m_swapchain->m_dirtyHdrMetadata = true;
|
||||
m_swapchain->m_hdrMetadata = *pHDRMetadata;
|
||||
|
||||
if (m_swapchain->m_wctx && m_swapchain->m_wctx->presenter)
|
||||
m_swapchain->m_wctx->presenter->setHdrMetadata(*pHDRMetadata);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -169,8 +169,6 @@ namespace dxvk {
|
||||
|
||||
uint32_t m_frameLatencyCap = 0;
|
||||
|
||||
bool m_dirty = true;
|
||||
|
||||
HWND m_window = nullptr;
|
||||
HMONITOR m_monitor = nullptr;
|
||||
|
||||
@ -185,7 +183,6 @@ namespace dxvk {
|
||||
VkColorSpaceKHR m_colorspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||
|
||||
std::optional<VkHdrMetadataEXT> m_hdrMetadata;
|
||||
bool m_dirtyHdrMetadata = true;
|
||||
bool m_unlockAdditionalFormats = false;
|
||||
|
||||
D3D9VkExtSwapchain m_swapchainExt;
|
||||
@ -194,7 +191,7 @@ namespace dxvk {
|
||||
|
||||
void SynchronizePresent();
|
||||
|
||||
void RecreateSwapChain();
|
||||
void RecreateSurface();
|
||||
|
||||
void CreatePresenter();
|
||||
|
||||
@ -212,13 +209,8 @@ namespace dxvk {
|
||||
|
||||
uint32_t GetActualFrameLatency();
|
||||
|
||||
uint32_t PickFormats(
|
||||
D3D9Format Format,
|
||||
VkSurfaceFormatKHR* pDstFormats);
|
||||
VkSurfaceFormatKHR GetSurfaceFormat();
|
||||
|
||||
uint32_t PickImageCount(
|
||||
UINT Preferred);
|
||||
|
||||
void NormalizePresentParameters(D3DPRESENT_PARAMETERS* pPresentParams);
|
||||
|
||||
void NotifyDisplayRefreshRate(
|
||||
@ -236,7 +228,9 @@ namespace dxvk {
|
||||
|
||||
HRESULT RestoreDisplayMode(HMONITOR hMonitor);
|
||||
|
||||
bool UpdatePresentRegion(const RECT* pSourceRect, const RECT* pDestRect);
|
||||
void UpdatePresentRegion(const RECT* pSourceRect, const RECT* pDestRect);
|
||||
|
||||
void UpdatePresentParameters();
|
||||
|
||||
VkExtent2D GetPresentExtent();
|
||||
|
||||
|
@ -307,14 +307,12 @@ namespace dxvk {
|
||||
|
||||
void DxvkDevice::presentImage(
|
||||
const Rc<Presenter>& presenter,
|
||||
VkPresentModeKHR presentMode,
|
||||
uint64_t frameId,
|
||||
DxvkSubmitStatus* status) {
|
||||
status->result = VK_NOT_READY;
|
||||
|
||||
DxvkPresentInfo presentInfo = { };
|
||||
presentInfo.presenter = presenter;
|
||||
presentInfo.presentMode = presentMode;
|
||||
presentInfo.frameId = frameId;
|
||||
m_submissionQueue.present(presentInfo, status);
|
||||
|
||||
|
@ -485,13 +485,11 @@ 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 [in] frameId Optional frame ID
|
||||
* \param [out] status Present status
|
||||
*/
|
||||
void presentImage(
|
||||
const Rc<Presenter>& presenter,
|
||||
VkPresentModeKHR presentMode,
|
||||
uint64_t frameId,
|
||||
DxvkSubmitStatus* status);
|
||||
|
||||
|
@ -52,26 +52,51 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
PresenterInfo Presenter::info() const {
|
||||
return m_info;
|
||||
VkResult Presenter::checkSwapChainStatus() {
|
||||
std::lock_guard lock(m_surfaceMutex);
|
||||
|
||||
if (!m_swapchain)
|
||||
return recreateSwapChain();
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
VkResult Presenter::acquireNextImage(PresenterSync& sync, Rc<DxvkImage>& image) {
|
||||
PresenterSync& semaphores = m_semaphores.at(m_frameIndex);
|
||||
sync = semaphores;
|
||||
std::lock_guard lock(m_surfaceMutex);
|
||||
|
||||
// Ensure that the swap chain gets recreated if it is dirty
|
||||
updateSwapChain();
|
||||
|
||||
// Don't acquire more than one image at a time
|
||||
if (m_acquireStatus == VK_NOT_READY) {
|
||||
waitForSwapchainFence(semaphores);
|
||||
if (m_acquireStatus == VK_NOT_READY && m_swapchain) {
|
||||
PresenterSync sync = m_semaphores.at(m_frameIndex);
|
||||
|
||||
waitForSwapchainFence(sync);
|
||||
|
||||
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 != VK_SUCCESS && m_acquireStatus != VK_SUBOPTIMAL_KHR)
|
||||
return m_acquireStatus;
|
||||
// If the swap chain is out of date, recreate it and retry. It
|
||||
// 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
|
||||
// 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);
|
||||
|
||||
return m_acquireStatus;
|
||||
}
|
||||
|
||||
|
||||
VkResult Presenter::presentImage(
|
||||
VkPresentModeKHR mode,
|
||||
uint64_t frameId) {
|
||||
VkResult Presenter::presentImage(uint64_t frameId) {
|
||||
PresenterSync& currSync = m_semaphores.at(m_frameIndex);
|
||||
|
||||
VkPresentIdKHR presentId = { VK_STRUCTURE_TYPE_PRESENT_ID_KHR };
|
||||
@ -104,7 +134,7 @@ namespace dxvk {
|
||||
|
||||
VkSwapchainPresentModeInfoEXT modeInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT };
|
||||
modeInfo.swapchainCount = 1;
|
||||
modeInfo.pPresentModes = &mode;
|
||||
modeInfo.pPresentModes = &m_presentMode;
|
||||
|
||||
VkPresentInfoKHR info = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
|
||||
info.waitSemaphoreCount = 1;
|
||||
@ -146,10 +176,7 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
void Presenter::signalFrame(
|
||||
VkResult result,
|
||||
VkPresentModeKHR mode,
|
||||
uint64_t frameId) {
|
||||
void Presenter::signalFrame(VkResult result, uint64_t frameId) {
|
||||
if (m_signal == nullptr || !frameId)
|
||||
return;
|
||||
|
||||
@ -158,7 +185,7 @@ namespace dxvk {
|
||||
|
||||
PresenterFrame frame = { };
|
||||
frame.result = result;
|
||||
frame.mode = mode;
|
||||
frame.mode = m_presentMode;
|
||||
frame.frameId = frameId;
|
||||
|
||||
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;
|
||||
|
||||
if (m_swapchain)
|
||||
destroySwapchain();
|
||||
|
||||
if (m_surface) {
|
||||
vr = createSwapChain(desc);
|
||||
vr = createSwapChain();
|
||||
|
||||
if (vr == VK_ERROR_SURFACE_LOST_KHR)
|
||||
destroySurface();
|
||||
@ -189,14 +304,27 @@ namespace dxvk {
|
||||
vr = createSurface();
|
||||
|
||||
if (vr == VK_SUCCESS)
|
||||
vr = createSwapChain(desc);
|
||||
vr = createSwapChain();
|
||||
}
|
||||
|
||||
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 };
|
||||
fullScreenExclusiveInfo.fullScreenExclusive = m_fullscreenMode;
|
||||
|
||||
@ -229,25 +357,22 @@ namespace dxvk {
|
||||
|
||||
// Select image extent based on current surface capabilities, and return
|
||||
// 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) {
|
||||
m_info.imageCount = 0;
|
||||
m_info.format = { VK_FORMAT_UNDEFINED, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
if (!imageExtent.width || !imageExtent.height)
|
||||
return VK_NOT_READY;
|
||||
|
||||
// Select format based on swap chain properties
|
||||
if ((status = getSupportedFormats(formats)))
|
||||
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
|
||||
if ((status = getSupportedPresentModes(modes)))
|
||||
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
|
||||
// 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 };
|
||||
presentModeInfo.pNext = const_cast<void*>(std::exchange(surfaceInfo.pNext, &presentModeInfo));
|
||||
presentModeInfo.presentMode = m_info.presentMode;
|
||||
presentModeInfo.presentMode = m_presentMode;
|
||||
|
||||
caps.pNext = &compatibleModeInfo;
|
||||
|
||||
@ -324,8 +449,6 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
// 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 };
|
||||
fullScreenInfo.fullScreenExclusive = m_fullscreenMode;
|
||||
|
||||
@ -335,17 +458,17 @@ namespace dxvk {
|
||||
|
||||
VkSwapchainCreateInfoKHR swapInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };
|
||||
swapInfo.surface = m_surface;
|
||||
swapInfo.minImageCount = m_info.imageCount;
|
||||
swapInfo.imageFormat = m_info.format.format;
|
||||
swapInfo.imageColorSpace = m_info.format.colorSpace;
|
||||
swapInfo.imageExtent = m_info.imageExtent;
|
||||
swapInfo.minImageCount = pickImageCount(minImageCount, maxImageCount);
|
||||
swapInfo.imageFormat = surfaceFormat.format;
|
||||
swapInfo.imageColorSpace = surfaceFormat.colorSpace;
|
||||
swapInfo.imageExtent = imageExtent;
|
||||
swapInfo.imageArrayLayers = 1;
|
||||
swapInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
|
||||
| VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
swapInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
swapInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
swapInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||
swapInfo.presentMode = m_info.presentMode;
|
||||
swapInfo.presentMode = m_presentMode;
|
||||
swapInfo.clipped = VK_TRUE;
|
||||
|
||||
if (m_device->features().extFullScreenExclusive)
|
||||
@ -356,26 +479,23 @@ namespace dxvk {
|
||||
|
||||
Logger::info(str::format(
|
||||
"Presenter: Actual swap chain properties:"
|
||||
"\n Format: ", m_info.format.format,
|
||||
"\n Color space: ", m_info.format.colorSpace,
|
||||
"\n Present mode: ", m_info.presentMode, " (dynamic: ", (dynamicModes.empty() ? "no)" : "yes)"),
|
||||
"\n Buffer size: ", m_info.imageExtent.width, "x", m_info.imageExtent.height,
|
||||
"\n Image count: ", m_info.imageCount));
|
||||
"\n Format: ", swapInfo.imageFormat,
|
||||
"\n Color space: ", swapInfo.imageColorSpace,
|
||||
"\n Present mode: ", swapInfo.presentMode, " (dynamic: ", (dynamicModes.empty() ? "no)" : "yes)"),
|
||||
"\n Buffer size: ", swapInfo.imageExtent.width, "x", swapInfo.imageExtent.height,
|
||||
"\n Image count: ", swapInfo.minImageCount));
|
||||
|
||||
if ((status = m_vkd->vkCreateSwapchainKHR(m_vkd->device(),
|
||||
&swapInfo, nullptr, &m_swapchain)))
|
||||
return status;
|
||||
|
||||
// Acquire images and create views
|
||||
// Import actual swap chain images
|
||||
std::vector<VkImage> images;
|
||||
|
||||
if ((status = getSwapImages(images)))
|
||||
return status;
|
||||
|
||||
// Update actual image count
|
||||
m_info.imageCount = images.size();
|
||||
|
||||
for (uint32_t i = 0; i < m_info.imageCount; i++) {
|
||||
for (uint32_t i = 0; i < images.size(); i++) {
|
||||
std::string debugName = str::format("Vulkan swap image ", i);
|
||||
|
||||
DxvkImageCreateInfo imageInfo = { };
|
||||
@ -397,7 +517,7 @@ namespace dxvk {
|
||||
|
||||
// Create one set of semaphores per swap image, as well as a fence
|
||||
// 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) {
|
||||
// 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 {
|
||||
uint32_t numFormats = 0;
|
||||
|
||||
@ -586,42 +651,148 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
VkSurfaceFormatKHR Presenter::pickFormat(
|
||||
VkSurfaceFormatKHR Presenter::pickSurfaceFormat(
|
||||
uint32_t numSupported,
|
||||
const VkSurfaceFormatKHR* pSupported,
|
||||
uint32_t numDesired,
|
||||
const VkSurfaceFormatKHR* pDesired) {
|
||||
if (numDesired > 0) {
|
||||
// If the implementation allows us to freely choose
|
||||
// the format, we'll just use the preferred format.
|
||||
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];
|
||||
}
|
||||
}
|
||||
const VkSurfaceFormatKHR& desired) {
|
||||
VkSurfaceFormatKHR result = { };
|
||||
result.colorSpace = pickColorSpace(numSupported, pSupported, desired.colorSpace);
|
||||
result.format = pickFormat(numSupported, pSupported, result.colorSpace, desired.format);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 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++) {
|
||||
auto currFlags = lookupFormatInfo(pSupported[j].format)->flags;
|
||||
VkColorSpaceKHR Presenter::pickColorSpace(
|
||||
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)
|
||||
== (prefFlags & DxvkFormatFlag::ColorSpaceSrgb))
|
||||
return pSupported[j];
|
||||
{ VK_COLOR_SPACE_HDR10_ST2084_EXT, VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT },
|
||||
}};
|
||||
|
||||
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
|
||||
return pSupported[0];
|
||||
|
||||
Logger::warn(str::format("No fallback color space found for ", desired, ", using ", pSupported[0].colorSpace));
|
||||
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 minImageCount,
|
||||
uint32_t maxImageCount,
|
||||
uint32_t desired) {
|
||||
uint32_t maxImageCount) {
|
||||
uint32_t count = minImageCount + 1;
|
||||
|
||||
if (count < desired)
|
||||
count = desired;
|
||||
|
||||
|
||||
if (count > maxImageCount && maxImageCount != 0)
|
||||
count = maxImageCount;
|
||||
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -33,25 +33,7 @@ namespace dxvk {
|
||||
* an input during swap chain creation.
|
||||
*/
|
||||
struct PresenterDesc {
|
||||
VkExtent2D imageExtent = { };
|
||||
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;
|
||||
bool deferSurfaceCreation = false;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -78,6 +60,15 @@ namespace dxvk {
|
||||
VkResult result = VK_NOT_READY;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Format compatibility list
|
||||
*/
|
||||
struct PresenterFormatList {
|
||||
VkColorSpaceKHR colorSpace;
|
||||
size_t formatCount;
|
||||
const VkFormat* formats;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Vulkan presenter
|
||||
*
|
||||
@ -98,21 +89,29 @@ namespace dxvk {
|
||||
~Presenter();
|
||||
|
||||
/**
|
||||
* \brief Actual presenter info
|
||||
* \returns Swap chain properties
|
||||
* \brief Tests swap chain status
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* Potentially blocks the calling thread.
|
||||
* If this returns an error, the swap chain
|
||||
* must be recreated and a new image must
|
||||
* be acquired before proceeding.
|
||||
*
|
||||
* Tries to acquire an image from the underlying Vulkan
|
||||
* swapchain. May recreate the swapchain if any surface
|
||||
* properties or user-specified parameters have changed.
|
||||
* 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] 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(
|
||||
PresenterSync& sync,
|
||||
@ -121,17 +120,12 @@ namespace dxvk {
|
||||
/**
|
||||
* \brief Presents current image
|
||||
*
|
||||
* 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
|
||||
* Presents the last successfuly acquired image.
|
||||
* \param [in] frameId Frame number.
|
||||
* Must increase monotonically.
|
||||
* \returns Status of the operation
|
||||
*/
|
||||
VkResult presentImage(
|
||||
VkPresentModeKHR mode,
|
||||
uint64_t frameId);
|
||||
VkResult presentImage(uint64_t frameId);
|
||||
|
||||
/**
|
||||
* \brief Signals a given frame
|
||||
@ -141,34 +135,17 @@ namespace dxvk {
|
||||
* called before GPU work prior to the present submission has
|
||||
* completed in order to maintain consistency.
|
||||
* \param [in] result Presentation result
|
||||
* \param [in] mode Present mode
|
||||
* \param [in] frameId Frame number
|
||||
*/
|
||||
void signalFrame(
|
||||
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);
|
||||
void signalFrame(VkResult result, uint64_t frameId);
|
||||
|
||||
/**
|
||||
* \brief Changes sync interval
|
||||
*
|
||||
* If this returns an error, the swap chain must
|
||||
* be recreated.
|
||||
* Changes the Vulkan present mode as necessary.
|
||||
* \param [in] syncInterval New sync interval
|
||||
*/
|
||||
VkResult setSyncInterval(uint32_t syncInterval);
|
||||
void setSyncInterval(uint32_t syncInterval);
|
||||
|
||||
/**
|
||||
* \brief Changes maximum frame rate
|
||||
@ -179,16 +156,32 @@ namespace dxvk {
|
||||
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
|
||||
* a swap chain as the surface size can reach zero, and no
|
||||
* presentation can be performed.
|
||||
* \returns \c true if the presenter has a swap chain.
|
||||
* If the Vulkan surface does not natively support the given
|
||||
* parameter combo, it will try to select a format and color
|
||||
* space with similar properties.
|
||||
* \param [in] format Preferred surface format
|
||||
*/
|
||||
bool hasSwapChain() const {
|
||||
return m_swapchain;
|
||||
}
|
||||
void setSurfaceFormat(VkSurfaceFormatKHR format);
|
||||
|
||||
/**
|
||||
* \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
|
||||
@ -199,12 +192,13 @@ namespace dxvk {
|
||||
bool supportsColorSpace(VkColorSpaceKHR colorspace);
|
||||
|
||||
/**
|
||||
* \brief Sets HDR metadata
|
||||
* \brief Invalidates Vulkan surface
|
||||
*
|
||||
* Updated HDR metadata will be applied on the next \c acquire.
|
||||
* \param [in] hdrMetadata HDR Metadata
|
||||
* This will cause the Vulkan surface to be destroyed and
|
||||
* 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:
|
||||
|
||||
@ -214,7 +208,7 @@ namespace dxvk {
|
||||
Rc<vk::InstanceFn> m_vki;
|
||||
Rc<vk::DeviceFn> m_vkd;
|
||||
|
||||
PresenterInfo m_info = { };
|
||||
dxvk::mutex m_surfaceMutex;
|
||||
PresenterSurfaceProc m_surfaceProc;
|
||||
|
||||
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
|
||||
@ -227,6 +221,15 @@ namespace dxvk {
|
||||
|
||||
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_frameIndex = 0;
|
||||
|
||||
@ -246,8 +249,11 @@ namespace dxvk {
|
||||
alignas(CACHE_LINE_SIZE)
|
||||
FpsLimiter m_fpsLimiter;
|
||||
|
||||
VkResult createSwapChain(
|
||||
const PresenterDesc& desc);
|
||||
void updateSwapChain();
|
||||
|
||||
VkResult recreateSwapChain();
|
||||
|
||||
VkResult createSwapChain();
|
||||
|
||||
VkResult getSupportedFormats(
|
||||
std::vector<VkSurfaceFormatKHR>& formats) const;
|
||||
@ -258,11 +264,21 @@ namespace dxvk {
|
||||
VkResult getSwapImages(
|
||||
std::vector<VkImage>& images);
|
||||
|
||||
VkSurfaceFormatKHR pickFormat(
|
||||
VkSurfaceFormatKHR pickSurfaceFormat(
|
||||
uint32_t numSupported,
|
||||
const VkSurfaceFormatKHR* pSupported,
|
||||
uint32_t numDesired,
|
||||
const VkSurfaceFormatKHR* pDesired);
|
||||
const VkSurfaceFormatKHR& desired);
|
||||
|
||||
VkColorSpaceKHR pickColorSpace(
|
||||
uint32_t numSupported,
|
||||
const VkSurfaceFormatKHR* pSupported,
|
||||
VkColorSpaceKHR desired);
|
||||
|
||||
VkFormat pickFormat(
|
||||
uint32_t numSupported,
|
||||
const VkSurfaceFormatKHR* pSupported,
|
||||
VkColorSpaceKHR colorSpace,
|
||||
VkFormat format);
|
||||
|
||||
VkPresentModeKHR pickPresentMode(
|
||||
uint32_t numSupported,
|
||||
@ -275,8 +291,7 @@ namespace dxvk {
|
||||
|
||||
uint32_t pickImageCount(
|
||||
uint32_t minImageCount,
|
||||
uint32_t maxImageCount,
|
||||
uint32_t desired);
|
||||
uint32_t maxImageCount);
|
||||
|
||||
VkResult createSurface();
|
||||
|
||||
|
@ -145,7 +145,7 @@ namespace dxvk {
|
||||
entry.result = entry.submit.cmdList->submit(m_semaphores, m_timelines);
|
||||
entry.timelines = m_timelines;
|
||||
} 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)
|
||||
@ -235,8 +235,7 @@ namespace dxvk {
|
||||
// Signal the frame and then immediately destroy the reference.
|
||||
// This is necessary since the front-end may want to explicitly
|
||||
// destroy the presenter object.
|
||||
entry.present.presenter->signalFrame(entry.result,
|
||||
entry.present.presentMode, entry.present.frameId);
|
||||
entry.present.presenter->signalFrame(entry.result, entry.present.frameId);
|
||||
entry.present.presenter = nullptr;
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,6 @@ namespace dxvk {
|
||||
*/
|
||||
struct DxvkPresentInfo {
|
||||
Rc<Presenter> presenter;
|
||||
VkPresentModeKHR presentMode;
|
||||
uint64_t frameId;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user