1
0
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:
Joshua Ashton 2023-06-24 05:49:17 +01:00 committed by Joshie
parent 026aa49ef8
commit 2f72115f91
3 changed files with 75 additions and 48 deletions

View File

@ -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;

View File

@ -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(

View File

@ -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;