mirror of
https://github.com/doitsujin/dxvk.git
synced 2024-12-11 19:24:11 +01:00
[d3d9] Keep 1 presenter per swapchain window
Some apps such as level editors such as Hammer World Editor, some GUI apps/launchers etc use window overrides in presentation. Previously we'd remake a new surface every time, which was incredibly slow making these apps basically unusable. Now we keep one surface + swapchain + image views around per window/window override we have, along with the frame latency objs + frame counter. (Obviously an app may present to multiple windows in a frame, so for frame latency purposes we track that per-window.
This commit is contained in:
parent
026aa49ef8
commit
2f72115f91
@ -128,6 +128,7 @@ namespace dxvk {
|
|||||||
constexpr static VkDeviceSize StagingBufferSize = 4ull << 20;
|
constexpr static VkDeviceSize StagingBufferSize = 4ull << 20;
|
||||||
|
|
||||||
friend class D3D9SwapChainEx;
|
friend class D3D9SwapChainEx;
|
||||||
|
friend struct D3D9WindowContext;
|
||||||
friend class D3D9ConstantBuffer;
|
friend class D3D9ConstantBuffer;
|
||||||
friend class D3D9UserDefinedAnnotation;
|
friend class D3D9UserDefinedAnnotation;
|
||||||
friend class DxvkD3D8Bridge;
|
friend class DxvkD3D8Bridge;
|
||||||
|
@ -28,13 +28,14 @@ namespace dxvk {
|
|||||||
, m_device (pDevice->GetDXVKDevice())
|
, m_device (pDevice->GetDXVKDevice())
|
||||||
, m_context (m_device->createContext(DxvkContextType::Supplementary))
|
, m_context (m_device->createContext(DxvkContextType::Supplementary))
|
||||||
, m_frameLatencyCap (pDevice->GetOptions()->maxFrameLatency)
|
, m_frameLatencyCap (pDevice->GetOptions()->maxFrameLatency)
|
||||||
, m_frameLatencySignal(new sync::Fence(m_frameId))
|
|
||||||
, m_dialog (pDevice->GetOptions()->enableDialogMode)
|
, m_dialog (pDevice->GetOptions()->enableDialogMode)
|
||||||
, m_swapchainExt (this) {
|
, m_swapchainExt (this) {
|
||||||
this->NormalizePresentParameters(pPresentParams);
|
this->NormalizePresentParameters(pPresentParams);
|
||||||
m_presentParams = *pPresentParams;
|
m_presentParams = *pPresentParams;
|
||||||
m_window = m_presentParams.hDeviceWindow;
|
m_window = m_presentParams.hDeviceWindow;
|
||||||
|
|
||||||
|
UpdateWindowCtx();
|
||||||
|
|
||||||
UpdatePresentRegion(nullptr, nullptr);
|
UpdatePresentRegion(nullptr, nullptr);
|
||||||
|
|
||||||
if (m_window) {
|
if (m_window) {
|
||||||
@ -137,20 +138,19 @@ namespace dxvk {
|
|||||||
if (options->presentInterval >= 0)
|
if (options->presentInterval >= 0)
|
||||||
presentInterval = options->presentInterval;
|
presentInterval = options->presentInterval;
|
||||||
|
|
||||||
HWND window = m_presentParams.hDeviceWindow;
|
m_window = m_presentParams.hDeviceWindow;
|
||||||
if (hDestWindowOverride != nullptr)
|
if (hDestWindowOverride != nullptr)
|
||||||
window = hDestWindowOverride;
|
m_window = hDestWindowOverride;
|
||||||
|
|
||||||
|
UpdateWindowCtx();
|
||||||
|
|
||||||
bool recreate = false;
|
bool recreate = false;
|
||||||
recreate |= m_presenter == nullptr;
|
recreate |= m_wctx->presenter == nullptr;
|
||||||
recreate |= window != m_window;
|
|
||||||
recreate |= m_dialog != m_lastDialog;
|
recreate |= m_dialog != m_lastDialog;
|
||||||
|
|
||||||
m_window = window;
|
if (m_wctx->presenter != nullptr) {
|
||||||
|
m_dirty |= m_wctx->presenter->setSyncInterval(presentInterval) != VK_SUCCESS;
|
||||||
if (m_presenter != nullptr) {
|
m_dirty |= !m_wctx->presenter->hasSwapChain();
|
||||||
m_dirty |= m_presenter->setSyncInterval(presentInterval) != VK_SUCCESS;
|
|
||||||
m_dirty |= !m_presenter->hasSwapChain();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_dirty |= UpdatePresentRegion(pSourceRect, pDestRect);
|
m_dirty |= UpdatePresentRegion(pSourceRect, pDestRect);
|
||||||
@ -161,7 +161,7 @@ namespace dxvk {
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
const bool useGDIFallback = m_partialCopy && !HasFrontBuffer();
|
const bool useGDIFallback = m_partialCopy && !HasFrontBuffer();
|
||||||
if (useGDIFallback)
|
if (useGDIFallback)
|
||||||
return PresentImageGDI(window);
|
return PresentImageGDI(m_window);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -174,7 +174,7 @@ namespace dxvk {
|
|||||||
// 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_presenter->hasSwapChain())
|
if (!m_wctx->presenter->hasSwapChain())
|
||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
|
|
||||||
PresentImage(presentInterval);
|
PresentImage(presentInterval);
|
||||||
@ -182,7 +182,7 @@ namespace dxvk {
|
|||||||
} catch (const DxvkError& e) {
|
} catch (const DxvkError& e) {
|
||||||
Logger::err(e.message());
|
Logger::err(e.message());
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
return PresentImageGDI(window);
|
return PresentImageGDI(m_window);
|
||||||
#else
|
#else
|
||||||
return D3DERR_DEVICEREMOVED;
|
return D3DERR_DEVICEREMOVED;
|
||||||
#endif
|
#endif
|
||||||
@ -684,8 +684,10 @@ namespace dxvk {
|
|||||||
if (hWindow == nullptr)
|
if (hWindow == nullptr)
|
||||||
hWindow = m_parent->GetWindow();
|
hWindow = m_parent->GetWindow();
|
||||||
|
|
||||||
if (m_presentParams.hDeviceWindow == hWindow) {
|
if (m_presenters.count(hWindow)) {
|
||||||
m_presenter = nullptr;
|
if (m_wctx == &m_presenters[hWindow])
|
||||||
|
m_wctx = nullptr;
|
||||||
|
m_presenters.erase(hWindow);
|
||||||
|
|
||||||
m_device->waitForSubmission(&m_presentStatus);
|
m_device->waitForSubmission(&m_presentStatus);
|
||||||
m_device->waitForIdle();
|
m_device->waitForIdle();
|
||||||
@ -758,22 +760,22 @@ namespace dxvk {
|
|||||||
SynchronizePresent();
|
SynchronizePresent();
|
||||||
|
|
||||||
// Presentation semaphores and WSI swap chain image
|
// Presentation semaphores and WSI swap chain image
|
||||||
PresenterInfo info = m_presenter->info();
|
PresenterInfo info = m_wctx->presenter->info();
|
||||||
PresenterSync sync;
|
PresenterSync sync;
|
||||||
|
|
||||||
uint32_t imageIndex = 0;
|
uint32_t imageIndex = 0;
|
||||||
|
|
||||||
VkResult status = m_presenter->acquireNextImage(sync, imageIndex);
|
VkResult status = m_wctx->presenter->acquireNextImage(sync, imageIndex);
|
||||||
|
|
||||||
while (status != VK_SUCCESS && status != VK_SUBOPTIMAL_KHR) {
|
while (status != VK_SUCCESS && status != VK_SUBOPTIMAL_KHR) {
|
||||||
RecreateSwapChain();
|
RecreateSwapChain();
|
||||||
|
|
||||||
info = m_presenter->info();
|
info = m_wctx->presenter->info();
|
||||||
status = m_presenter->acquireNextImage(sync, imageIndex);
|
status = m_wctx->presenter->acquireNextImage(sync, imageIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_hdrMetadata && m_dirtyHdrMetadata) {
|
if (m_hdrMetadata && m_dirtyHdrMetadata) {
|
||||||
m_presenter->setHdrMetadata(*m_hdrMetadata);
|
m_wctx->presenter->setHdrMetadata(*m_hdrMetadata);
|
||||||
m_dirtyHdrMetadata = false;
|
m_dirtyHdrMetadata = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -789,7 +791,7 @@ namespace dxvk {
|
|||||||
{ uint32_t(m_dstRect.right - m_dstRect.left), uint32_t(m_dstRect.bottom - m_dstRect.top) } };
|
{ uint32_t(m_dstRect.right - m_dstRect.left), uint32_t(m_dstRect.bottom - m_dstRect.top) } };
|
||||||
|
|
||||||
m_blitter->presentImage(m_context.ptr(),
|
m_blitter->presentImage(m_context.ptr(),
|
||||||
m_imageViews.at(imageIndex), dstRect,
|
m_wctx->imageViews.at(imageIndex), dstRect,
|
||||||
swapImageView, srcRect);
|
swapImageView, srcRect);
|
||||||
|
|
||||||
if (m_hud != nullptr)
|
if (m_hud != nullptr)
|
||||||
@ -812,7 +814,7 @@ namespace dxvk {
|
|||||||
void D3D9SwapChainEx::SubmitPresent(const PresenterSync& Sync, uint32_t Repeat) {
|
void D3D9SwapChainEx::SubmitPresent(const PresenterSync& Sync, uint32_t Repeat) {
|
||||||
// Bump frame ID
|
// Bump frame ID
|
||||||
if (!Repeat)
|
if (!Repeat)
|
||||||
m_frameId += 1;
|
m_wctx->frameId += 1;
|
||||||
|
|
||||||
// Present from CS thread so that we don't
|
// Present from CS thread so that we don't
|
||||||
// have to synchronize with it first.
|
// have to synchronize with it first.
|
||||||
@ -822,8 +824,8 @@ namespace dxvk {
|
|||||||
cRepeat = Repeat,
|
cRepeat = Repeat,
|
||||||
cSync = Sync,
|
cSync = Sync,
|
||||||
cHud = m_hud,
|
cHud = m_hud,
|
||||||
cPresentMode = m_presenter->info().presentMode,
|
cPresentMode = m_wctx->presenter->info().presentMode,
|
||||||
cFrameId = m_frameId,
|
cFrameId = m_wctx->frameId,
|
||||||
cCommandList = m_context->endRecording()
|
cCommandList = m_context->endRecording()
|
||||||
] (DxvkContext* ctx) {
|
] (DxvkContext* ctx) {
|
||||||
cCommandList->setWsiSemaphores(cSync);
|
cCommandList->setWsiSemaphores(cSync);
|
||||||
@ -834,7 +836,7 @@ namespace dxvk {
|
|||||||
|
|
||||||
uint64_t frameId = cRepeat ? 0 : cFrameId;
|
uint64_t frameId = cRepeat ? 0 : cFrameId;
|
||||||
|
|
||||||
m_device->presentImage(m_presenter,
|
m_device->presentImage(m_wctx->presenter,
|
||||||
cPresentMode, frameId, &m_presentStatus);
|
cPresentMode, frameId, &m_presentStatus);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -863,17 +865,17 @@ namespace dxvk {
|
|||||||
presenterDesc.numFormats = PickFormats(EnumerateFormat(m_presentParams.BackBufferFormat), presenterDesc.formats);
|
presenterDesc.numFormats = PickFormats(EnumerateFormat(m_presentParams.BackBufferFormat), presenterDesc.formats);
|
||||||
presenterDesc.fullScreenExclusive = PickFullscreenMode();
|
presenterDesc.fullScreenExclusive = PickFullscreenMode();
|
||||||
|
|
||||||
VkResult vr = m_presenter->recreateSwapChain(presenterDesc);
|
VkResult vr = m_wctx->presenter->recreateSwapChain(presenterDesc);
|
||||||
|
|
||||||
if (vr == VK_ERROR_SURFACE_LOST_KHR) {
|
if (vr == VK_ERROR_SURFACE_LOST_KHR) {
|
||||||
vr = m_presenter->recreateSurface([this] (VkSurfaceKHR* surface) {
|
vr = m_wctx->presenter->recreateSurface([this] (VkSurfaceKHR* surface) {
|
||||||
return CreateSurface(surface);
|
return CreateSurface(surface);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (vr)
|
if (vr)
|
||||||
throw DxvkError(str::format("D3D9SwapChainEx: Failed to recreate surface: ", vr));
|
throw DxvkError(str::format("D3D9SwapChainEx: Failed to recreate surface: ", vr));
|
||||||
|
|
||||||
vr = m_presenter->recreateSwapChain(presenterDesc);
|
vr = m_wctx->presenter->recreateSwapChain(presenterDesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vr)
|
if (vr)
|
||||||
@ -896,8 +898,8 @@ namespace dxvk {
|
|||||||
presenterDesc.numFormats = PickFormats(EnumerateFormat(m_presentParams.BackBufferFormat), presenterDesc.formats);
|
presenterDesc.numFormats = PickFormats(EnumerateFormat(m_presentParams.BackBufferFormat), presenterDesc.formats);
|
||||||
presenterDesc.fullScreenExclusive = PickFullscreenMode();
|
presenterDesc.fullScreenExclusive = PickFullscreenMode();
|
||||||
|
|
||||||
m_presenter = new Presenter(m_device, m_frameLatencySignal, presenterDesc);
|
m_wctx->presenter = new Presenter(m_device, m_wctx->frameLatencySignal, presenterDesc);
|
||||||
m_presenter->setFrameRateLimit(m_parent->GetOptions()->maxFrameRate);
|
m_wctx->presenter->setFrameRateLimit(m_parent->GetOptions()->maxFrameRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -912,10 +914,10 @@ namespace dxvk {
|
|||||||
|
|
||||||
|
|
||||||
void D3D9SwapChainEx::CreateRenderTargetViews() {
|
void D3D9SwapChainEx::CreateRenderTargetViews() {
|
||||||
PresenterInfo info = m_presenter->info();
|
PresenterInfo info = m_wctx->presenter->info();
|
||||||
|
|
||||||
m_imageViews.clear();
|
m_wctx->imageViews.clear();
|
||||||
m_imageViews.resize(info.imageCount);
|
m_wctx->imageViews.resize(info.imageCount);
|
||||||
|
|
||||||
DxvkImageCreateInfo imageInfo;
|
DxvkImageCreateInfo imageInfo;
|
||||||
imageInfo.type = VK_IMAGE_TYPE_2D;
|
imageInfo.type = VK_IMAGE_TYPE_2D;
|
||||||
@ -943,13 +945,13 @@ namespace dxvk {
|
|||||||
viewInfo.numLayers = 1;
|
viewInfo.numLayers = 1;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < info.imageCount; i++) {
|
for (uint32_t i = 0; i < info.imageCount; i++) {
|
||||||
VkImage imageHandle = m_presenter->getImage(i).image;
|
VkImage imageHandle = m_wctx->presenter->getImage(i).image;
|
||||||
|
|
||||||
Rc<DxvkImage> image = new DxvkImage(
|
Rc<DxvkImage> image = new DxvkImage(
|
||||||
m_device.ptr(), imageInfo, imageHandle,
|
m_device.ptr(), imageInfo, imageHandle,
|
||||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||||
|
|
||||||
m_imageViews[i] = new DxvkImageView(
|
m_wctx->imageViews[i] = new DxvkImageView(
|
||||||
m_device->vkd(), image, viewInfo);
|
m_device->vkd(), image, viewInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -963,6 +965,20 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void D3D9SwapChainEx::UpdateWindowCtx() {
|
||||||
|
if (!m_presenters.count(m_window)) {
|
||||||
|
auto res = m_presenters.emplace(
|
||||||
|
std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(m_window),
|
||||||
|
std::forward_as_tuple());
|
||||||
|
|
||||||
|
auto& wctx = res.first->second;
|
||||||
|
wctx.frameLatencySignal = new sync::Fence(wctx.frameId);
|
||||||
|
}
|
||||||
|
m_wctx = &m_presenters[m_window];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
HRESULT D3D9SwapChainEx::CreateBackBuffers(uint32_t NumBackBuffers) {
|
HRESULT D3D9SwapChainEx::CreateBackBuffers(uint32_t NumBackBuffers) {
|
||||||
// Explicitly destroy current swap image before
|
// Explicitly destroy current swap image before
|
||||||
// creating a new one to free up resources
|
// creating a new one to free up resources
|
||||||
@ -1065,9 +1081,9 @@ namespace dxvk {
|
|||||||
|
|
||||||
void D3D9SwapChainEx::SyncFrameLatency() {
|
void D3D9SwapChainEx::SyncFrameLatency() {
|
||||||
// Wait for the sync event so that we respect the maximum frame latency
|
// Wait for the sync event so that we respect the maximum frame latency
|
||||||
m_frameLatencySignal->wait(m_frameId - GetActualFrameLatency());
|
m_wctx->frameLatencySignal->wait(m_wctx->frameId - GetActualFrameLatency());
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D9SwapChainEx::SetApiName(const char* name) {
|
void D3D9SwapChainEx::SetApiName(const char* name) {
|
||||||
m_apiName = name;
|
m_apiName = name;
|
||||||
CreateHud();
|
CreateHud();
|
||||||
@ -1280,8 +1296,9 @@ namespace dxvk {
|
|||||||
|| dstRect.bottom - dstRect.top != LONG(height);
|
|| dstRect.bottom - dstRect.top != LONG(height);
|
||||||
|
|
||||||
bool recreate =
|
bool recreate =
|
||||||
m_swapchainExtent.width != width
|
m_wctx->presenter == nullptr
|
||||||
|| m_swapchainExtent.height != height;
|
|| 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;
|
||||||
@ -1330,7 +1347,7 @@ namespace dxvk {
|
|||||||
|
|
||||||
BOOL STDMETHODCALLTYPE D3D9VkExtSwapchain::CheckColorSpaceSupport(
|
BOOL STDMETHODCALLTYPE D3D9VkExtSwapchain::CheckColorSpaceSupport(
|
||||||
VkColorSpaceKHR ColorSpace) {
|
VkColorSpaceKHR ColorSpace) {
|
||||||
return m_swapchain->m_presenter->supportsColorSpace(ColorSpace);
|
return m_swapchain->m_wctx->presenter->supportsColorSpace(ColorSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE D3D9VkExtSwapchain::SetColorSpace(
|
HRESULT STDMETHODCALLTYPE D3D9VkExtSwapchain::SetColorSpace(
|
||||||
|
@ -50,6 +50,14 @@ namespace dxvk {
|
|||||||
D3D9SwapChainEx *m_swapchain;
|
D3D9SwapChainEx *m_swapchain;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct D3D9WindowContext {
|
||||||
|
Rc<Presenter> presenter;
|
||||||
|
std::vector<Rc<DxvkImageView>> imageViews;
|
||||||
|
|
||||||
|
uint64_t frameId = D3D9DeviceEx::MaxFrameLatency;
|
||||||
|
Rc<sync::Fence> frameLatencySignal;
|
||||||
|
};
|
||||||
|
|
||||||
using D3D9SwapChainExBase = D3D9DeviceChild<IDirect3DSwapChain9Ex>;
|
using D3D9SwapChainExBase = D3D9DeviceChild<IDirect3DSwapChain9Ex>;
|
||||||
class D3D9SwapChainEx final : public D3D9SwapChainExBase {
|
class D3D9SwapChainEx final : public D3D9SwapChainExBase {
|
||||||
static constexpr uint32_t NumControlPoints = 256;
|
static constexpr uint32_t NumControlPoints = 256;
|
||||||
@ -124,6 +132,8 @@ namespace dxvk {
|
|||||||
|
|
||||||
void SetApiName(const char* name);
|
void SetApiName(const char* name);
|
||||||
|
|
||||||
|
void UpdateWindowCtx();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
enum BindingIds : uint32_t {
|
enum BindingIds : uint32_t {
|
||||||
@ -138,7 +148,11 @@ namespace dxvk {
|
|||||||
Rc<DxvkContext> m_context;
|
Rc<DxvkContext> m_context;
|
||||||
Rc<DxvkSwapchainBlitter> m_blitter;
|
Rc<DxvkSwapchainBlitter> m_blitter;
|
||||||
|
|
||||||
Rc<Presenter> m_presenter;
|
std::unordered_map<
|
||||||
|
HWND,
|
||||||
|
D3D9WindowContext> m_presenters;
|
||||||
|
|
||||||
|
D3D9WindowContext* m_wctx = nullptr;
|
||||||
|
|
||||||
Rc<hud::Hud> m_hud;
|
Rc<hud::Hud> m_hud;
|
||||||
|
|
||||||
@ -151,12 +165,7 @@ namespace dxvk {
|
|||||||
|
|
||||||
DxvkSubmitStatus m_presentStatus;
|
DxvkSubmitStatus m_presentStatus;
|
||||||
|
|
||||||
std::vector<Rc<DxvkImageView>> m_imageViews;
|
uint32_t m_frameLatencyCap = 0;
|
||||||
|
|
||||||
|
|
||||||
uint64_t m_frameId = D3D9DeviceEx::MaxFrameLatency;
|
|
||||||
uint32_t m_frameLatencyCap = 0;
|
|
||||||
Rc<sync::Fence> m_frameLatencySignal;
|
|
||||||
|
|
||||||
bool m_dirty = true;
|
bool m_dirty = true;
|
||||||
bool m_dialog = false;
|
bool m_dialog = false;
|
||||||
|
Loading…
Reference in New Issue
Block a user