[d3d9] Don't swap buffers for SWAPEFFECT_COPY & DISCARD with 1 backbuffer

This commit is contained in:
Robin Kertels 2023-05-03 19:32:32 +02:00 committed by Joshie
parent 83a294285e
commit 5443a2f9f5
3 changed files with 54 additions and 23 deletions

View File

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

View File

@ -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<D3D9Surface*>(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<DxvkImage> swapImage = m_backBuffers[0]->GetCommonTexture()->GetImage();
Rc<DxvkImageView> 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<uint32_t>(m_dstRect.right - m_dstRect.left, 1u),
std::max<uint32_t>(m_dstRect.bottom - m_dstRect.top, 1u) };
return m_swapchainExtent;
}

View File

@ -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<D3D9Surface, false>& 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;
}
};
}
}