From e1d063c74e938730b425ec1cab20a27fa0f3b332 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Sat, 10 Jun 2023 01:18:05 +0200 Subject: [PATCH] [d3d9] Reject Reset if there's any remaining DEFAULT resources --- src/d3d9/d3d9_common_buffer.cpp | 5 ++ src/d3d9/d3d9_common_buffer.h | 2 + src/d3d9/d3d9_common_texture.cpp | 3 ++ src/d3d9/d3d9_common_texture.h | 1 + src/d3d9/d3d9_device.cpp | 90 +++++++++++++++++++++++++++++++- src/d3d9/d3d9_device.h | 36 +++---------- src/d3d9/d3d9_options.cpp | 4 +- src/d3d9/d3d9_options.h | 2 +- src/d3d9/d3d9_stateblock.cpp | 3 ++ src/d3d9/d3d9_stateblock.h | 2 + src/d3d9/d3d9_swapchain.cpp | 3 ++ src/util/config/config.cpp | 4 +- 12 files changed, 119 insertions(+), 36 deletions(-) diff --git a/src/d3d9/d3d9_common_buffer.cpp b/src/d3d9/d3d9_common_buffer.cpp index adeb957e44b..d61517f6305 100644 --- a/src/d3d9/d3d9_common_buffer.cpp +++ b/src/d3d9/d3d9_common_buffer.cpp @@ -20,6 +20,11 @@ namespace dxvk { m_dirtyRange = D3D9Range(0, m_desc.Size); } + D3D9CommonBuffer::~D3D9CommonBuffer() { + if (m_desc.Pool == D3DPOOL_DEFAULT) + m_parent->DecrementLosableCounter(); + } + HRESULT D3D9CommonBuffer::Lock( UINT OffsetToLock, diff --git a/src/d3d9/d3d9_common_buffer.h b/src/d3d9/d3d9_common_buffer.h index cf672692495..d8bd3b9a621 100644 --- a/src/d3d9/d3d9_common_buffer.h +++ b/src/d3d9/d3d9_common_buffer.h @@ -78,6 +78,8 @@ namespace dxvk { D3D9DeviceEx* pDevice, const D3D9_BUFFER_DESC* pDesc); + ~D3D9CommonBuffer(); + HRESULT Lock( UINT OffsetToLock, UINT SizeToLock, diff --git a/src/d3d9/d3d9_common_texture.cpp b/src/d3d9/d3d9_common_texture.cpp index 8a0b1bd0636..4ab9537ab45 100644 --- a/src/d3d9/d3d9_common_texture.cpp +++ b/src/d3d9/d3d9_common_texture.cpp @@ -98,6 +98,9 @@ namespace dxvk { m_device->ChangeReportedMemory(m_size); m_device->RemoveMappedTexture(this); + + if (m_desc.IsLosable) + m_device->DecrementLosableCounter(); } diff --git a/src/d3d9/d3d9_common_texture.h b/src/d3d9/d3d9_common_texture.h index 58cb854ee10..22b105fe5bb 100644 --- a/src/d3d9/d3d9_common_texture.h +++ b/src/d3d9/d3d9_common_texture.h @@ -48,6 +48,7 @@ namespace dxvk { bool IsBackBuffer; bool IsAttachmentOnly; bool IsLockable; + bool IsLosable; }; struct D3D9ColorView { diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 71136dbd054..4d039b07e67 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -412,12 +412,29 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D9DeviceEx::Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) { D3D9DeviceLock lock = LockDevice(); + /* + * Before calling the IDirect3DDevice9::Reset method for a device, + * an application should release any explicit render targets, + * depth stencil surfaces, additional swap chains, state blocks, + * and D3DPOOL_DEFAULT resources associated with the device. + */ + if (unlikely(m_losableResourceCounter.load() != 0 && !IsExtended())) { + Logger::info("Device reset failed because device still has alive losable resources: Device not reset"); + m_deviceLostState = D3D9DeviceLostState::NotReset; + return D3DERR_INVALIDCALL; + } + Logger::info("Device reset"); m_deviceLostState = D3D9DeviceLostState::Ok; HRESULT hr = ResetSwapChain(pPresentationParameters, nullptr); - if (FAILED(hr)) + if (FAILED(hr)) { + if (!IsExtended()) { + Logger::info("Device reset failed: Device not reset"); + m_deviceLostState = D3D9DeviceLostState::NotReset; + } return hr; + } ResetState(pPresentationParameters); @@ -516,6 +533,7 @@ namespace dxvk { desc.MultisampleQuality = 0; desc.IsBackBuffer = FALSE; desc.IsAttachmentOnly = FALSE; + desc.IsLosable = Pool == D3DPOOL_DEFAULT; // Docs: // Textures placed in the D3DPOOL_DEFAULT pool cannot be locked // unless they are dynamic textures or they are private, FOURCC, driver formats. @@ -542,6 +560,9 @@ namespace dxvk { m_initializer->InitTexture(texture->GetCommonTexture(), initialData); *ppTexture = texture.ref(); + if (desc.IsLosable) + m_losableResourceCounter++; + return D3D_OK; } catch (const DxvkError& e) { @@ -583,6 +604,7 @@ namespace dxvk { desc.MultisampleQuality = 0; desc.IsBackBuffer = FALSE; desc.IsAttachmentOnly = FALSE; + desc.IsLosable = Pool == D3DPOOL_DEFAULT; // Docs: // Textures placed in the D3DPOOL_DEFAULT pool cannot be locked // unless they are dynamic textures or they are private, FOURCC, driver formats. @@ -597,6 +619,9 @@ namespace dxvk { const Com texture = new D3D9Texture3D(this, &desc); m_initializer->InitTexture(texture->GetCommonTexture()); *ppVolumeTexture = texture.ref(); + + if (desc.IsLosable) + m_losableResourceCounter++; return D3D_OK; } @@ -637,6 +662,7 @@ namespace dxvk { desc.MultisampleQuality = 0; desc.IsBackBuffer = FALSE; desc.IsAttachmentOnly = FALSE; + desc.IsLosable = Pool == D3DPOOL_DEFAULT; // Docs: // Textures placed in the D3DPOOL_DEFAULT pool cannot be locked // unless they are dynamic textures or they are private, FOURCC, driver formats. @@ -651,6 +677,9 @@ namespace dxvk { const Com texture = new D3D9TextureCube(this, &desc); m_initializer->InitTexture(texture->GetCommonTexture()); *ppCubeTexture = texture.ref(); + + if (desc.IsLosable) + m_losableResourceCounter++; return D3D_OK; } @@ -691,6 +720,9 @@ namespace dxvk { const Com buffer = new D3D9VertexBuffer(this, &desc); m_initializer->InitBuffer(buffer->GetCommonBuffer()); *ppVertexBuffer = buffer.ref(); + if (desc.Pool == D3DPOOL_DEFAULT) + m_losableResourceCounter++; + return D3D_OK; } catch (const DxvkError & e) { @@ -729,6 +761,9 @@ namespace dxvk { const Com buffer = new D3D9IndexBuffer(this, &desc); m_initializer->InitBuffer(buffer->GetCommonBuffer()); *ppIndexBuffer = buffer.ref(); + if (desc.Pool == D3DPOOL_DEFAULT) + m_losableResourceCounter++; + return D3D_OK; } catch (const DxvkError & e) { @@ -2254,6 +2289,8 @@ namespace dxvk { try { const Com sb = new D3D9StateBlock(this, ConvertStateBlockType(Type)); *ppSB = sb.ref(); + m_losableResourceCounter++; + return D3D_OK; } catch (const DxvkError & e) { @@ -2284,6 +2321,7 @@ namespace dxvk { return D3DERR_INVALIDCALL; *ppSB = m_recorder.ref(); + m_losableResourceCounter++; m_recorder = nullptr; return D3D_OK; @@ -3610,6 +3648,7 @@ namespace dxvk { desc.IsBackBuffer = FALSE; desc.IsAttachmentOnly = TRUE; desc.IsLockable = Lockable; + desc.IsLosable = TRUE; if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc))) return D3DERR_INVALIDCALL; @@ -3618,6 +3657,8 @@ namespace dxvk { const Com surface = new D3D9Surface(this, &desc, nullptr, pSharedHandle); m_initializer->InitTexture(surface->GetCommonTexture()); *ppSurface = surface.ref(); + m_losableResourceCounter++; + return D3D_OK; } catch (const DxvkError& e) { @@ -3654,6 +3695,7 @@ namespace dxvk { desc.MultisampleQuality = 0; desc.IsBackBuffer = FALSE; desc.IsAttachmentOnly = Pool == D3DPOOL_DEFAULT; + desc.IsLosable = Pool == D3DPOOL_DEFAULT; // Docs: Off-screen plain surfaces are always lockable, regardless of their pool types. desc.IsLockable = TRUE; @@ -3667,6 +3709,10 @@ namespace dxvk { const Com surface = new D3D9Surface(this, &desc, nullptr, pSharedHandle); m_initializer->InitTexture(surface->GetCommonTexture()); *ppSurface = surface.ref(); + + if (desc.IsLosable) + m_losableResourceCounter++; + return D3D_OK; } catch (const DxvkError& e) { @@ -3705,6 +3751,7 @@ namespace dxvk { desc.MultisampleQuality = MultisampleQuality; desc.IsBackBuffer = FALSE; desc.IsAttachmentOnly = TRUE; + desc.IsLosable = TRUE; // Docs don't say anything, so just assume it's lockable. desc.IsLockable = TRUE; @@ -3715,6 +3762,8 @@ namespace dxvk { const Com surface = new D3D9Surface(this, &desc, nullptr, pSharedHandle); m_initializer->InitTexture(surface->GetCommonTexture()); *ppSurface = surface.ref(); + m_losableResourceCounter++; + return D3D_OK; } catch (const DxvkError& e) { @@ -3776,6 +3825,7 @@ namespace dxvk { try { auto* swapchain = new D3D9SwapChainEx(this, pPresentationParameters, pFullscreenDisplayMode); *ppSwapChain = ref(swapchain); + m_losableResourceCounter++; } catch (const DxvkError & e) { Logger::err(e.message()); @@ -7453,6 +7503,7 @@ namespace dxvk { desc.MultisampleQuality = pPresentationParameters->MultiSampleQuality; desc.IsBackBuffer = FALSE; desc.IsAttachmentOnly = TRUE; + desc.IsLosable = FALSE; // Docs: Also note that - unlike textures - swap chain back buffers, render targets [..] can be locked desc.IsLockable = TRUE; @@ -7563,6 +7614,43 @@ namespace dxvk { #endif } + //////////////////////////////////// + // D3D9 Device Lost + //////////////////////////////////// + + void D3D9DeviceEx::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 D3D9DeviceEx::NotifyWindowActivated(HWND window, bool activated) { + D3D9DeviceLock lock = LockDevice(); + + if (likely(!m_d3d9Options.deviceLossOnFocusLoss || 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; + } + } + //////////////////////////////////// // D3D9 Device Specialization State //////////////////////////////////// diff --git a/src/d3d9/d3d9_device.h b/src/d3d9/d3d9_device.h index eba85c0b806..3e09dbb0b18 100644 --- a/src/d3d9/d3d9_device.h +++ b/src/d3d9/d3d9_device.h @@ -961,41 +961,16 @@ namespace dxvk { void TouchMappedTexture(D3D9CommonTexture* pTexture); void RemoveMappedTexture(D3D9CommonTexture* pTexture); + // Device Lost bool IsDeviceLost() const { return m_deviceLostState != D3D9DeviceLostState::Ok; } - void NotifyFullscreen(HWND window, bool fullscreen) { - D3D9DeviceLock lock = LockDevice(); + void NotifyFullscreen(HWND window, bool fullscreen); + void NotifyWindowActivated(HWND window, bool activated); - 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; - } + void DecrementLosableCounter() { + m_losableResourceCounter--; } private: @@ -1366,6 +1341,7 @@ namespace dxvk { D3D9DeviceLostState m_deviceLostState = D3D9DeviceLostState::Ok; HWND m_fullscreenWindow = NULL; + std::atomic m_losableResourceCounter = { 0 }; #ifdef D3D9_ALLOW_UNMAPPING lru_list m_mappedTextures; diff --git a/src/d3d9/d3d9_options.cpp b/src/d3d9/d3d9_options.cpp index 83a23e5544e..fad34384973 100644 --- a/src/d3d9/d3d9_options.cpp +++ b/src/d3d9/d3d9_options.cpp @@ -71,8 +71,8 @@ namespace dxvk { this->deviceLocalConstantBuffers = config.getOption ("d3d9.deviceLocalConstantBuffers", false); 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); + this->textureMemory = config.getOption ("d3d9.textureMemory", 100) << 20; + this->deviceLossOnFocusLoss = config.getOption ("d3d9.deviceLossOnFocusLoss", 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 923245ed5f3..3d89a74ea6d 100644 --- a/src/d3d9/d3d9_options.h +++ b/src/d3d9/d3d9_options.h @@ -146,7 +146,7 @@ namespace dxvk { std::string shaderDumpPath; /// Enable emulation of device loss when a fullscreen app loses focus - bool deviceLost; + bool deviceLossOnFocusLoss; }; } diff --git a/src/d3d9/d3d9_stateblock.cpp b/src/d3d9/d3d9_stateblock.cpp index bf6b6718b33..9bb7181b966 100644 --- a/src/d3d9/d3d9_stateblock.cpp +++ b/src/d3d9/d3d9_stateblock.cpp @@ -17,6 +17,9 @@ namespace dxvk { CaptureType(Type); } + D3D9StateBlock::~D3D9StateBlock() { + m_parent->DecrementLosableCounter(); + } HRESULT STDMETHODCALLTYPE D3D9StateBlock::QueryInterface( REFIID riid, diff --git a/src/d3d9/d3d9_stateblock.h b/src/d3d9/d3d9_stateblock.h index 5fee4ae57f7..50826617548 100644 --- a/src/d3d9/d3d9_stateblock.h +++ b/src/d3d9/d3d9_stateblock.h @@ -89,6 +89,8 @@ namespace dxvk { D3D9StateBlock(D3D9DeviceEx* pDevice, D3D9StateBlockType Type); + ~D3D9StateBlock(); + HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject) final; diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp index acfa0573aff..37884e368a1 100644 --- a/src/d3d9/d3d9_swapchain.cpp +++ b/src/d3d9/d3d9_swapchain.cpp @@ -71,6 +71,8 @@ namespace dxvk { m_device->waitForSubmission(&m_presentStatus); m_device->waitForIdle(); + + m_parent->DecrementLosableCounter(); } @@ -977,6 +979,7 @@ namespace dxvk { desc.Discard = FALSE; desc.IsBackBuffer = TRUE; desc.IsAttachmentOnly = FALSE; + desc.IsLosable = TRUE; // Docs: Also note that - unlike textures - swap chain back buffers, render targets [..] can be locked desc.IsLockable = TRUE; diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 9e9446c5fd8..f1c072945d6 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -481,7 +481,7 @@ namespace dxvk { { "d3d9.customVendorId", "1002" }, { "dxgi.emulateUMA", "True" }, { "d3d9.supportDFFormats", "False" }, - { "d3d9.deviceLost", "True" }, + { "d3d9.deviceLostOnFocusLoss", "True" }, }} }, /* Battlefield 2 (bad z-pass) */ { R"(\\BF2\.exe$)", {{ @@ -752,7 +752,7 @@ namespace dxvk { /* DC Universe Online * * Freezes after alt tabbing */ { R"(\\DCGAME\.EXE$)", {{ - { "d3d9.deviceLost", "True" }, + { "d3d9.deviceLostOnFocusLoss", "True" }, }} }, /* Halo Online * * Black textures */