From b4366db39845801a55a33f92626e3bf207a3ae0d Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Mon, 27 Mar 2023 17:46:41 +0200 Subject: [PATCH] [d3d9] Implement rudimentary device loss --- src/d3d9/d3d9_device.cpp | 37 +++++++++++++++++++--------- src/d3d9/d3d9_device.h | 48 ++++++++++++++++++++++++++++++++++++- src/d3d9/d3d9_options.cpp | 1 + src/d3d9/d3d9_options.h | 3 +++ src/d3d9/d3d9_query.cpp | 10 ++++++-- src/d3d9/d3d9_swapchain.cpp | 13 ++++++++++ src/d3d9/d3d9_window.cpp | 21 ++++++++++++---- 7 files changed, 114 insertions(+), 19 deletions(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 2ff296741..29e3b977d 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -211,8 +211,16 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D9DeviceEx::TestCooperativeLevel() { + D3D9DeviceLock lock = LockDevice(); + // Equivelant of D3D11/DXGI present tests. We can always present. - return D3D_OK; + if (likely(m_deviceLostState == D3D9DeviceLostState::Ok)) { + return D3D_OK; + } else if (m_deviceLostState == D3D9DeviceLostState::NotReset) { + return D3DERR_DEVICENOTRESET; + } else { + return D3DERR_DEVICELOST; + } } @@ -382,13 +390,14 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D9DeviceEx::Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) { D3D9DeviceLock lock = LockDevice(); + Logger::info("Device reset"); + m_deviceLostState = D3D9DeviceLostState::Ok; + HRESULT hr = ResetSwapChain(pPresentationParameters, nullptr); if (FAILED(hr)) return hr; - hr = ResetState(pPresentationParameters); - if (FAILED(hr)) - return hr; + ResetState(pPresentationParameters); Flush(); SynchronizeCsThread(DxvkCsThread::SynchronizeAll); @@ -911,6 +920,10 @@ namespace dxvk { IDirect3DSurface9* pDestSurface) { D3D9DeviceLock lock = LockDevice(); + if (unlikely(IsDeviceLost())) { + return D3DERR_DEVICELOST; + } + D3D9Surface* src = static_cast(pRenderTarget); D3D9Surface* dst = static_cast(pDestSurface); @@ -2365,10 +2378,12 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D9DeviceEx::ValidateDevice(DWORD* pNumPasses) { + D3D9DeviceLock lock = LockDevice(); + if (pNumPasses != nullptr) *pNumPasses = 1; - return D3D_OK; + return IsDeviceLost() ? D3DERR_DEVICELOST : D3D_OK; } @@ -3726,6 +3741,10 @@ namespace dxvk { if (!m_implicitSwapchain->GetPresentParams()->Windowed) return D3DERR_INVALIDCALL; + if (unlikely(IsDeviceLost())) { + return D3DERR_DEVICELOST; + } + m_implicitSwapchain->Invalidate(pPresentationParameters->hDeviceWindow); try { @@ -7094,7 +7113,7 @@ namespace dxvk { } - HRESULT D3D9DeviceEx::ResetState(D3DPRESENT_PARAMETERS* pPresentationParameters) { + void D3D9DeviceEx::ResetState(D3DPRESENT_PARAMETERS* pPresentationParameters) { if (!pPresentationParameters->EnableAutoDepthStencil) SetDepthStencilSurface(nullptr); @@ -7335,8 +7354,6 @@ namespace dxvk { UpdateVertexBoolSpec(0u); UpdatePixelBoolSpec(0u); UpdateCommonSamplerSpec(0u, 0u, 0u); - - return D3D_OK; } @@ -7410,9 +7427,7 @@ namespace dxvk { if (FAILED(hr)) return hr; - hr = ResetState(pPresentationParameters); - if (FAILED(hr)) - return hr; + ResetState(pPresentationParameters); Flush(); SynchronizeCsThread(DxvkCsThread::SynchronizeAll); diff --git a/src/d3d9/d3d9_device.h b/src/d3d9/d3d9_device.h index bd379266d..aaf7773b9 100644 --- a/src/d3d9/d3d9_device.h +++ b/src/d3d9/d3d9_device.h @@ -87,6 +87,12 @@ namespace dxvk { using D3D9DeviceFlags = Flags; + enum class D3D9DeviceLostState { + Ok = 0, + Lost = 1, + NotReset = 2, + }; + struct D3D9DrawInfo { uint32_t vertexCount; uint32_t instanceCount; @@ -933,7 +939,7 @@ namespace dxvk { const D3D9ConstantLayout& GetVertexConstantLayout() { return m_vsLayout; } const D3D9ConstantLayout& GetPixelConstantLayout() { return m_psLayout; } - HRESULT ResetState(D3DPRESENT_PARAMETERS* pPresentationParameters); + void ResetState(D3DPRESENT_PARAMETERS* pPresentationParameters); HRESULT ResetSwapChain(D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode); HRESULT InitialReset(D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode); @@ -950,6 +956,43 @@ namespace dxvk { void TouchMappedTexture(D3D9CommonTexture* pTexture); void RemoveMappedTexture(D3D9CommonTexture* pTexture); + bool IsDeviceLost() const { + return m_deviceLostState != D3D9DeviceLostState::Ok; + } + + void NotifyFullscreen(HWND window, bool fullscreen) { + D3D9DeviceLock lock = LockDevice(); + + if (fullscreen) { + if (unlikely(window != m_fullscreenWindow && m_fullscreenWindow != NULL)) { + Logger::warn("Multiple fullscreen windows detected."); + } + m_fullscreenWindow = window; + } else { + if (unlikely(m_fullscreenWindow != window)) { + Logger::warn("Window was not fullscreen in the first place."); + } else { + m_fullscreenWindow = 0; + } + } + } + + void NotifyWindowActivated(HWND window, bool activated) { + D3D9DeviceLock lock = LockDevice(); + + if (likely(!m_d3d9Options.deviceLost || IsExtended())) + return; + + if (activated && m_deviceLostState == D3D9DeviceLostState::Lost) { + Logger::info("Device not reset"); + m_deviceLostState = D3D9DeviceLostState::NotReset; + } else if (!activated && m_deviceLostState != D3D9DeviceLostState::Lost && m_fullscreenWindow == window) { + Logger::info("Device lost"); + m_deviceLostState = D3D9DeviceLostState::Lost; + m_fullscreenWindow = NULL; + } + } + private: DxvkCsChunkRef AllocCsChunk() { @@ -1317,6 +1360,9 @@ namespace dxvk { Direct3DState9 m_state; + D3D9DeviceLostState m_deviceLostState = D3D9DeviceLostState::Ok; + HWND m_fullscreenWindow = NULL; + #ifdef D3D9_ALLOW_UNMAPPING lru_list m_mappedTextures; #endif diff --git a/src/d3d9/d3d9_options.cpp b/src/d3d9/d3d9_options.cpp index 0ecb38953..86ba41d7a 100644 --- a/src/d3d9/d3d9_options.cpp +++ b/src/d3d9/d3d9_options.cpp @@ -74,6 +74,7 @@ namespace dxvk { this->allowDirectBufferMapping = config.getOption ("d3d9.allowDirectBufferMapping", true); this->seamlessCubes = config.getOption ("d3d9.seamlessCubes", false); this->textureMemory = config.getOption ("d3d9.textureMemory", 100) << 20; + this->deviceLost = config.getOption ("d3d9.deviceLost", false); std::string floatEmulation = Config::toLower(config.getOption("d3d9.floatEmulation", "auto")); if (floatEmulation == "strict") { diff --git a/src/d3d9/d3d9_options.h b/src/d3d9/d3d9_options.h index 592c27d18..f23e69133 100644 --- a/src/d3d9/d3d9_options.h +++ b/src/d3d9/d3d9_options.h @@ -158,6 +158,9 @@ namespace dxvk { /// Shader dump path std::string shaderDumpPath; + + /// Enable emulation of device loss when a fullscreen app loses focus + bool deviceLost; }; } diff --git a/src/d3d9/d3d9_query.cpp b/src/d3d9/d3d9_query.cpp index 238c7e353..31624a087 100644 --- a/src/d3d9/d3d9_query.cpp +++ b/src/d3d9/d3d9_query.cpp @@ -132,6 +132,14 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D9Query::GetData(void* pData, DWORD dwSize, DWORD dwGetDataFlags) { + D3D9DeviceLock lock = m_parent->LockDevice(); + + bool flush = dwGetDataFlags & D3DGETDATA_FLUSH; + + if (unlikely(m_parent->IsDeviceLost())) { + return flush ? D3DERR_DEVICELOST : S_FALSE; + } + if (m_state == D3D9_VK_QUERY_CACHED) { // Query data was already retrieved once. // Use cached query data to prevent having to check the VK event @@ -148,8 +156,6 @@ namespace dxvk { HRESULT hr = this->GetQueryData(pData, dwSize); - bool flush = dwGetDataFlags & D3DGETDATA_FLUSH; - // If we get S_FALSE and it's not from the fact // they didn't call end, do some flushy stuff... if (flush && hr == S_FALSE && m_state != D3D9_VK_QUERY_BEGUN) { diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp index 017b562e2..5192429f1 100644 --- a/src/d3d9/d3d9_swapchain.cpp +++ b/src/d3d9/d3d9_swapchain.cpp @@ -103,6 +103,9 @@ namespace dxvk { DWORD dwFlags) { D3D9DeviceLock lock = m_parent->LockDevice(); + if (unlikely(m_parent->IsDeviceLost())) + return D3DERR_DEVICELOST; + // If we have no backbuffers, error out. // This handles the case where a ::Reset failed due to OOM // or whatever. @@ -237,6 +240,10 @@ namespace dxvk { if (unlikely(dstTexInfo->Desc()->Pool != D3DPOOL_SYSTEMMEM && dstTexInfo->Desc()->Pool != D3DPOOL_SCRATCH)) return D3DERR_INVALIDCALL; + + if (unlikely(m_parent->IsDeviceLost())) { + return D3DERR_DEVICELOST; + } VkExtent3D dstTexExtent = dstTexInfo->GetExtentMip(dst->GetMipLevel()); VkExtent3D srcTexExtent = srcTexInfo->GetExtentMip(0); @@ -540,6 +547,8 @@ namespace dxvk { this->LeaveFullscreenMode(); } else { + m_parent->NotifyFullscreen(m_window, true); + if (changeFullscreen) { hr = this->EnterFullscreenMode(pPresentParams, pFullscreenDisplayMode); if (FAILED(hr)) @@ -1151,6 +1160,8 @@ namespace dxvk { return D3DERR_INVALIDCALL; } + m_parent->NotifyFullscreen(m_window, true); + return D3D_OK; } @@ -1170,6 +1181,8 @@ namespace dxvk { Logger::err("D3D9: LeaveFullscreenMode: Failed to exit fullscreen mode"); return D3DERR_NOTAVAILABLE; } + + m_parent->NotifyFullscreen(m_window, false); return D3D_OK; } diff --git a/src/d3d9/d3d9_window.cpp b/src/d3d9/d3d9_window.cpp index d52607976..4e44a8dae 100644 --- a/src/d3d9/d3d9_window.cpp +++ b/src/d3d9/d3d9_window.cpp @@ -10,6 +10,7 @@ namespace dxvk bool unicode; bool filter; bool activateProcessed; + bool deactivateProcessed; WNDPROC proc; D3D9SwapChainEx* swapchain; }; @@ -53,11 +54,14 @@ namespace dxvk DefWindowProcW, DefWindowProcA, unicode, window, message, wparam, lparam); + + D3D9DeviceEx* device = windowData.swapchain->GetParent(); + if (message == WM_DESTROY) ResetWindowProc(window); else if (message == WM_ACTIVATEAPP) { D3DDEVICE_CREATION_PARAMETERS create_parms; - windowData.swapchain->GetDevice()->GetCreationParameters(&create_parms); + device->GetCreationParameters(&create_parms); if (!(create_parms.BehaviorFlags & D3DCREATE_NOWINDOWCHANGES)) { D3D9WindowMessageFilter filter(window); @@ -70,19 +74,24 @@ namespace dxvk windowData.swapchain->GetPresentParameters(¶ms); SetWindowPos(window, nullptr, rect.left, rect.top, params.BackBufferWidth, params.BackBufferHeight, SWP_NOACTIVATE | SWP_NOZORDER); - SetActivateProcessed(window, true); } else if (!wparam) { if (IsWindowVisible(window)) ShowWindow(window, SW_MINIMIZE); - SetActivateProcessed(window, false); } } + + if ((wparam && !windowData.activateProcessed) + || (!wparam && !windowData.deactivateProcessed)) { + device->NotifyWindowActivated(window, wparam); + } + + SetActivateProcessed(window, !!wparam); } else if (message == WM_SIZE) { D3DDEVICE_CREATION_PARAMETERS create_parms; - windowData.swapchain->GetDevice()->GetCreationParameters(&create_parms); + device->GetCreationParameters(&create_parms); if (!(create_parms.BehaviorFlags & D3DCREATE_NOWINDOWCHANGES) && !IsIconic(window)) PostMessageW(window, WM_ACTIVATEAPP, 1, GetCurrentThreadId()); @@ -137,8 +146,10 @@ namespace dxvk { std::lock_guard lock(g_windowProcMapMutex); auto it = g_windowProcMap.find(window); - if (it != g_windowProcMap.end()) + if (it != g_windowProcMap.end()) { it->second.activateProcessed = processed; + it->second.deactivateProcessed = !processed; + } } #else D3D9WindowMessageFilter::D3D9WindowMessageFilter(HWND window, bool filter) {