diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 2b340c74..329e9e7b 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -1093,7 +1093,12 @@ namespace dxvk { if (unlikely(iSwapChain != 0)) return D3DERR_INVALIDCALL; - return m_implicitSwapchain->GetFrontBufferData(pDestSurface); + D3D9DeviceLock lock = LockDevice(); + + // In windowed mode, GetFrontBufferData takes a screenshot of the entire screen. + // We use the last used swapchain as a workaround. + // Total War: Medieval 2 relies on this. + return m_mostRecentlyUsedSwapchain->GetFrontBufferData(pDestSurface); } @@ -7839,6 +7844,7 @@ namespace dxvk { } else m_implicitSwapchain = new D3D9SwapChainEx(this, pPresentationParameters, pFullscreenDisplayMode); + m_mostRecentlyUsedSwapchain = m_implicitSwapchain.ptr(); if (pPresentationParameters->EnableAutoDepthStencil) { D3D9_COMMON_TEXTURE_DESC desc; diff --git a/src/d3d9/d3d9_device.h b/src/d3d9/d3d9_device.h index 2fc74c83..90beb034 100644 --- a/src/d3d9/d3d9_device.h +++ b/src/d3d9/d3d9_device.h @@ -1241,6 +1241,34 @@ namespace dxvk { uint64_t GetCurrentSequenceNumber(); + /** + * @brief Get the swapchain that was used the most recently for presenting + * Has to be externally synchronized. + * + * @return D3D9SwapChainEx* Swapchain + */ + D3D9SwapChainEx* GetMostRecentlyUsedSwapchain() { + return m_mostRecentlyUsedSwapchain; + } + + /** + * @brief Set the swapchain that was used the most recently for presenting + * Has to be externally synchronized. + * + * @param swapchain Swapchain + */ + void SetMostRecentlyUsedSwapchain(D3D9SwapChainEx* swapchain) { + m_mostRecentlyUsedSwapchain = swapchain; + } + + /** + * @brief Reset the most recently swapchain back to the implicit one + * Has to be externally synchronized. + */ + void ResetMostRecentlyUsedSwapchain() { + m_mostRecentlyUsedSwapchain = m_implicitSwapchain.ptr(); + } + Com m_parent; D3DDEVTYPE m_deviceType; HWND m_window; @@ -1403,6 +1431,8 @@ namespace dxvk { HWND m_fullscreenWindow = NULL; std::atomic m_losableResourceCounter = { 0 }; + D3D9SwapChainEx* m_mostRecentlyUsedSwapchain = nullptr; + #ifdef D3D9_ALLOW_UNMAPPING lru_list m_mappedTextures; #endif diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp index a19ca55a..57724330 100644 --- a/src/d3d9/d3d9_swapchain.cpp +++ b/src/d3d9/d3d9_swapchain.cpp @@ -65,6 +65,16 @@ namespace dxvk { if (this_thread::isInModuleDetachment()) return; + { + // Locking here and in Device::GetFrontBufferData + // ensures that other threads don't accidentally access a stale pointer. + D3D9DeviceLock lock = m_parent->LockDevice(); + + if (m_parent->GetMostRecentlyUsedSwapchain() == this) { + m_parent->ResetMostRecentlyUsedSwapchain(); + } + } + DestroyBackBuffers(); ResetWindowProc(m_window); @@ -112,6 +122,8 @@ namespace dxvk { DWORD dwFlags) { D3D9DeviceLock lock = m_parent->LockDevice(); + m_parent->SetMostRecentlyUsedSwapchain(this); + if (unlikely(m_parent->IsDeviceLost())) return D3DERR_DEVICELOST;