diff --git a/src/d3d9/d3d9_include.h b/src/d3d9/d3d9_include.h index e4e5f2af..896e7df9 100644 --- a/src/d3d9/d3d9_include.h +++ b/src/d3d9/d3d9_include.h @@ -44,6 +44,10 @@ #define D3DPRESENT_FORCEIMMEDIATE 0x00000100L #endif +#ifndef D3DSWAPEFFECT_COPY_VSYNC +#define D3DSWAPEFFECT_COPY_VSYNC 4 +#endif + // MinGW headers are broken. Who'dve guessed? #ifndef _MSC_VER typedef struct _D3DDEVINFO_RESOURCEMANAGER diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp index bec1a225..a66384c2 100644 --- a/src/d3d9/d3d9_swapchain.cpp +++ b/src/d3d9/d3d9_swapchain.cpp @@ -153,9 +153,9 @@ namespace dxvk { m_lastDialog = m_dialog; #ifdef _WIN32 - const bool useGDIFallback = m_partialCopy && m_presentParams.SwapEffect == D3DSWAPEFFECT_COPY; + const bool useGDIFallback = m_partialCopy && !HasFrontBuffer(); if (useGDIFallback) - return BlitGDI(window); + return PresentImageGDI(window); #endif try { @@ -176,7 +176,7 @@ namespace dxvk { } catch (const DxvkError& e) { Logger::err(e.message()); #ifdef _WIN32 - return BlitGDI(window); + return PresentImageGDI(window); #else return D3DERR_DEVICEREMOVED; #endif @@ -186,8 +186,11 @@ namespace dxvk { #ifdef _WIN32 #define DCX_USESTYLE 0x00010000 - HRESULT D3D9SwapChainEx::BlitGDI(HWND Window) { - if (!std::exchange(m_warnedAboutFallback, true)) + HRESULT D3D9SwapChainEx::PresentImageGDI(HWND Window) { + m_parent->EndFrame(); + m_parent->Flush(); + + if (!std::exchange(m_warnedGDIAboutFallback, true)) Logger::warn("Using GDI for swapchain presentation. This will impact performance."); HDC hDC; @@ -233,13 +236,18 @@ namespace dxvk { // of src onto a temp image of dst's extents, // then copy buffer back to dst (given dst is subresource) + // For SWAPEFFECT_COPY and windowed SWAPEFFECT_DISCARD with 1 backbuffer, we just copy the backbuffer data instead. + // We just copy from the backbuffer instead of the front buffer to avoid having to do another blit. + // This mostly impacts windowed mode and our implementation was not accurate in that case anyway as Windows D3D9 + // takes a screenshot of the entire screen. + D3D9Surface* dst = static_cast(pDestSurface); if (unlikely(dst == nullptr)) return D3DERR_INVALIDCALL; D3D9CommonTexture* dstTexInfo = dst->GetCommonTexture(); - D3D9CommonTexture* srcTexInfo = m_backBuffers.back()->GetCommonTexture(); + D3D9CommonTexture* srcTexInfo = GetFrontBuffer()->GetCommonTexture(); if (unlikely(dstTexInfo->Desc()->Pool != D3DPOOL_SYSTEMMEM && dstTexInfo->Desc()->Pool != D3DPOOL_SCRATCH)) return D3DERR_INVALIDCALL; @@ -730,8 +738,8 @@ namespace dxvk { m_parent->Flush(); // Retrieve the image and image view to present - auto swapImage = m_backBuffers[0]->GetCommonTexture()->GetImage(); - auto swapImageView = m_backBuffers[0]->GetImageView(false); + Rc swapImage = m_backBuffers[0]->GetCommonTexture()->GetImage(); + Rc swapImageView = m_backBuffers[0]->GetImageView(false); // Bump our frame id. ++m_frameId; @@ -821,7 +829,6 @@ namespace dxvk { RecreateSwapChain(m_vsync); } - void D3D9SwapChainEx::RecreateSwapChain(BOOL Vsync) { // Ensure that we can safely destroy the swap chain m_device->waitForSubmission(&m_presentStatus); @@ -954,7 +961,7 @@ namespace dxvk { // creating a new one to free up resources DestroyBackBuffers(); - int NumFrontBuffer = m_parent->GetOptions()->noExplicitFrontBuffer ? 0 : 1; + int NumFrontBuffer = HasFrontBuffer() ? 1 : 0; const uint32_t NumBuffers = NumBackBuffers + NumFrontBuffer; m_backBuffers.reserve(NumBuffers); @@ -1235,7 +1242,11 @@ namespace dxvk { } bool D3D9SwapChainEx::UpdatePresentRegion(const RECT* pSourceRect, const RECT* pDestRect) { - if (pSourceRect == nullptr) { + const bool isWindowed = m_presentParams.Windowed; + + // Tests show that present regions are ignored in fullscreen + + if (pSourceRect == nullptr || !isWindowed) { m_srcRect.top = 0; m_srcRect.left = 0; m_srcRect.right = m_presentParams.BackBufferWidth; @@ -1249,7 +1260,7 @@ namespace dxvk { wsi::getWindowSize(m_window, &width, &height); RECT dstRect; - if (pDestRect == nullptr) { + if (pDestRect == nullptr || !isWindowed) { // TODO: Should we hook WM_SIZE message for this? dstRect.top = 0; dstRect.left = 0; @@ -1266,21 +1277,18 @@ namespace dxvk { || dstRect.right - dstRect.left != LONG(width) || dstRect.bottom - dstRect.top != LONG(height); - bool recreate = - m_dstRect.left != dstRect.left - || m_dstRect.top != dstRect.top - || m_dstRect.right != dstRect.right - || m_dstRect.bottom != dstRect.bottom; + bool recreate = + m_swapchainExtent.width != width + || m_swapchainExtent.height != height; + m_swapchainExtent = { width, height }; m_dstRect = dstRect; return recreate; } VkExtent2D D3D9SwapChainEx::GetPresentExtent() { - return VkExtent2D { - std::max(m_dstRect.right - m_dstRect.left, 1u), - std::max(m_dstRect.bottom - m_dstRect.top, 1u) }; + return m_swapchainExtent; } diff --git a/src/d3d9/d3d9_swapchain.h b/src/d3d9/d3d9_swapchain.h index 2313dd60..85a4836e 100644 --- a/src/d3d9/d3d9_swapchain.h +++ b/src/d3d9/d3d9_swapchain.h @@ -41,7 +41,7 @@ namespace dxvk { DWORD dwFlags); #ifdef _WIN32 - HRESULT BlitGDI(HWND Window); + HRESULT PresentImageGDI(HWND Window); #endif HRESULT STDMETHODCALLTYPE GetFrontBufferData(IDirect3DSurface9* pDestSurface); @@ -107,6 +107,7 @@ namespace dxvk { RECT m_srcRect; RECT m_dstRect; + VkExtent2D m_swapchainExtent = { 0u, 0u }; bool m_partialCopy = false; DxvkSubmitStatus m_presentStatus; @@ -131,7 +132,7 @@ namespace dxvk { double m_displayRefreshRate = 0.0; - bool m_warnedAboutFallback = false; + bool m_warnedGDIAboutFallback = false; void PresentImage(UINT PresentInterval); @@ -197,6 +198,24 @@ namespace dxvk { std::string GetApiName(); + const Com& GetFrontBuffer() const { + return m_backBuffers.back(); + } + + bool HasFrontBuffer() const { + if (m_presentParams.SwapEffect == D3DSWAPEFFECT_COPY) + return false; + + if (m_presentParams.SwapEffect == D3DSWAPEFFECT_COPY_VSYNC) + return false; + + // Tests show that SWAPEEFFECT_DISCARD + 1 backbuffer in windowed mode behaves identically to SWAPEFFECT_COPY + // For SWAPEFFECT_COPY we don't swap buffers but do another blit to the front buffer instead. + if (m_presentParams.SwapEffect == D3DSWAPEFFECT_DISCARD && m_presentParams.BackBufferCount == 1 && m_presentParams.Windowed) + return false; + + return true; + } }; -} \ No newline at end of file +}