From b16deed95c0da32fe2edad9c6bad894ea87d1223 Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Mon, 25 Nov 2024 23:44:52 +0200 Subject: [PATCH 1/7] [d3d9] Only advertise SRCCOLOR2 & INVSRCCOLOR2 with 9Ex --- src/d3d8/d3d8_d3d9_util.h | 8 ++------ src/d3d9/d3d9_adapter.cpp | 4 ++-- src/d3d9/d3d9_device.cpp | 8 ++++++++ src/d3d9/d3d9_interface.cpp | 17 ++++++++++++++--- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/d3d8/d3d8_d3d9_util.h b/src/d3d8/d3d8_d3d9_util.h index d8ed4dbed80..96a83c10b80 100644 --- a/src/d3d8/d3d8_d3d9_util.h +++ b/src/d3d8/d3d8_d3d9_util.h @@ -49,13 +49,9 @@ namespace dxvk { & ~D3DPRASTERCAPS_DEPTHBIAS & ~D3DPRASTERCAPS_MULTISAMPLE_TOGGLE; - pCaps8->SrcBlendCaps &= ~D3DPBLENDCAPS_BLENDFACTOR - & ~D3DPBLENDCAPS_INVSRCCOLOR2 - & ~D3DPBLENDCAPS_SRCCOLOR2; + pCaps8->SrcBlendCaps &= ~D3DPBLENDCAPS_BLENDFACTOR; - pCaps8->DestBlendCaps &= ~D3DPBLENDCAPS_BLENDFACTOR - & ~D3DPBLENDCAPS_INVSRCCOLOR2 - & ~D3DPBLENDCAPS_SRCCOLOR2; + pCaps8->DestBlendCaps &= ~D3DPBLENDCAPS_BLENDFACTOR; pCaps8->LineCaps &= ~D3DLINECAPS_ANTIALIAS; diff --git a/src/d3d9/d3d9_adapter.cpp b/src/d3d9/d3d9_adapter.cpp index d166d7138a5..cebc9a43d78 100644 --- a/src/d3d9/d3d9_adapter.cpp +++ b/src/d3d9/d3d9_adapter.cpp @@ -413,8 +413,8 @@ namespace dxvk { | D3DPBLENDCAPS_BOTHSRCALPHA | D3DPBLENDCAPS_BOTHINVSRCALPHA | D3DPBLENDCAPS_BLENDFACTOR - | D3DPBLENDCAPS_INVSRCCOLOR2 - | D3DPBLENDCAPS_SRCCOLOR2; + | D3DPBLENDCAPS_SRCCOLOR2 + | D3DPBLENDCAPS_INVSRCCOLOR2; // Destination Blend Caps pCaps->DestBlendCaps = pCaps->SrcBlendCaps; // Alpha Comparison Caps diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index ab8111e1e19..84227c8f65b 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -309,6 +309,14 @@ namespace dxvk { // When in SWVP mode, 256 matrices can be used for indexed vertex blending pCaps->MaxVertexBlendMatrixIndex = m_isSWVP ? 255 : 8; + // Only 9Ex devices advertise D3DPBLENDCAPS_SRCCOLOR2 and D3DPBLENDCAPS_INVSRCCOLOR2 + if (!IsExtended()) { + pCaps->SrcBlendCaps &= ~D3DPBLENDCAPS_SRCCOLOR2 + & ~D3DPBLENDCAPS_INVSRCCOLOR2; + + pCaps->DestBlendCaps &= ~D3DPBLENDCAPS_SRCCOLOR2 + & ~D3DPBLENDCAPS_INVSRCCOLOR2; + } return D3D_OK; } diff --git a/src/d3d9/d3d9_interface.cpp b/src/d3d9/d3d9_interface.cpp index 1d3946cf70d..9e67628aecd 100644 --- a/src/d3d9/d3d9_interface.cpp +++ b/src/d3d9/d3d9_interface.cpp @@ -243,9 +243,20 @@ namespace dxvk { UINT Adapter, D3DDEVTYPE DeviceType, D3DCAPS9* pCaps) { - if (auto* adapter = GetAdapter(Adapter)) - return adapter->GetDeviceCaps( - DeviceType, pCaps); + if (auto* adapter = GetAdapter(Adapter)) { + adapter->GetDeviceCaps(DeviceType, pCaps); + + // Only 9Ex interfaces advertise D3DPBLENDCAPS_SRCCOLOR2 and D3DPBLENDCAPS_INVSRCCOLOR2 + if (!m_extended) { + pCaps->SrcBlendCaps &= ~D3DPBLENDCAPS_SRCCOLOR2 + & ~D3DPBLENDCAPS_INVSRCCOLOR2; + + pCaps->DestBlendCaps &= ~D3DPBLENDCAPS_SRCCOLOR2 + & ~D3DPBLENDCAPS_INVSRCCOLOR2; + } + + return D3D_OK; + } return D3DERR_INVALIDCALL; } From a8539a5a07391d2a3bff65561930c69234cbbba7 Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Tue, 26 Nov 2024 00:16:04 +0200 Subject: [PATCH 2/7] [d3d9] Prevent device child refcount underrun --- src/d3d9/d3d9_device_child.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/d3d9/d3d9_device_child.h b/src/d3d9/d3d9_device_child.h index 433a269ac8b..82b22ff71e5 100644 --- a/src/d3d9/d3d9_device_child.h +++ b/src/d3d9/d3d9_device_child.h @@ -25,6 +25,10 @@ namespace dxvk { } ULONG STDMETHODCALLTYPE Release() { + // ignore Release calls on objects with 0 refCount + if(unlikely(!this->m_refCount)) + return this->m_refCount; + uint32_t refCount = --this->m_refCount; if (unlikely(!refCount)) { auto* pDevice = GetDevice(); From 65ae3f7b2481f1215c75099ea3f5b17037b84377 Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Tue, 26 Nov 2024 00:59:38 +0200 Subject: [PATCH 3/7] [d3d9] Skip recording MultiplyTransform calls in state blocks --- src/d3d9/d3d9_device.cpp | 4 ---- src/d3d9/d3d9_stateblock.cpp | 9 --------- src/d3d9/d3d9_stateblock.h | 2 -- 3 files changed, 15 deletions(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 84227c8f65b..ed36ef52c1b 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -2033,10 +2033,6 @@ namespace dxvk { const uint32_t idx = GetTransformIndex(TransformState); - // D3D8 state blocks ignore capturing calls to MultiplyTransform(). - if (unlikely(!m_isD3D8Compatible && ShouldRecord())) - return m_recorder->MultiplyStateTransform(idx, pMatrix); - m_state.transforms[idx] = m_state.transforms[idx] * ConvertMatrix(pMatrix); m_flags.set(D3D9DeviceFlag::DirtyFFVertexData); diff --git a/src/d3d9/d3d9_stateblock.cpp b/src/d3d9/d3d9_stateblock.cpp index fb2033cdc83..85154e209c9 100644 --- a/src/d3d9/d3d9_stateblock.cpp +++ b/src/d3d9/d3d9_stateblock.cpp @@ -243,15 +243,6 @@ namespace dxvk { } - HRESULT D3D9StateBlock::MultiplyStateTransform(uint32_t idx, const D3DMATRIX* pMatrix) { - m_state.transforms[idx] = m_state.transforms[idx] * ConvertMatrix(pMatrix); - - m_captures.flags.set(D3D9CapturedStateFlag::Transforms); - m_captures.transforms.set(idx, true); - return D3D_OK; - } - - HRESULT D3D9StateBlock::SetViewport(const D3DVIEWPORT9* pViewport) { m_state.viewport = *pViewport; diff --git a/src/d3d9/d3d9_stateblock.h b/src/d3d9/d3d9_stateblock.h index 64bea6f2272..e21cc26df87 100644 --- a/src/d3d9/d3d9_stateblock.h +++ b/src/d3d9/d3d9_stateblock.h @@ -141,8 +141,6 @@ namespace dxvk { D3D9TextureStageStateTypes Type, DWORD Value); - HRESULT MultiplyStateTransform(uint32_t idx, const D3DMATRIX* pMatrix); - HRESULT SetViewport(const D3DVIEWPORT9* pViewport); HRESULT SetScissorRect(const RECT* pRect); From 10cf8c80ac7d1f260adfdd9ec5f7956d5be16e5f Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Tue, 26 Nov 2024 23:41:06 +0200 Subject: [PATCH 4/7] [d3d9] Adjust out of bounds clip plane index handling --- src/d3d9/d3d9_device.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index ed36ef52c1b..772f7c54c3d 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -2195,9 +2195,13 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetClipPlane(DWORD Index, const float* pPlane) { D3D9DeviceLock lock = LockDevice(); - if (unlikely(Index >= caps::MaxClipPlanes || !pPlane)) + if (unlikely(!pPlane)) return D3DERR_INVALIDCALL; + // Higher indexes will be capped to the last valid index + if (unlikely(Index >= caps::MaxClipPlanes)) + Index = caps::MaxClipPlanes - 1; + if (unlikely(ShouldRecord())) return m_recorder->SetClipPlane(Index, pPlane); @@ -2221,9 +2225,13 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetClipPlane(DWORD Index, float* pPlane) { D3D9DeviceLock lock = LockDevice(); - if (unlikely(Index >= caps::MaxClipPlanes || !pPlane)) + if (unlikely(!pPlane)) return D3DERR_INVALIDCALL; + // Higher indexes will be capped to the last valid index + if (unlikely(Index >= caps::MaxClipPlanes)) + Index = caps::MaxClipPlanes - 1; + for (uint32_t i = 0; i < 4; i++) pPlane[i] = m_state.clipPlanes[Index].coeff[i]; From 8e25b1a8b3e261a9974c76f652a9e82a0c985226 Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Wed, 27 Nov 2024 00:46:30 +0200 Subject: [PATCH 5/7] [d3d9] Validate supported formats in D3D9Surface::GetDC --- src/d3d9/d3d9_format.h | 12 ++++++++++++ src/d3d9/d3d9_surface.cpp | 11 +++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/d3d9/d3d9_format.h b/src/d3d9/d3d9_format.h index 617c2adc890..43d26553c0c 100644 --- a/src/d3d9/d3d9_format.h +++ b/src/d3d9/d3d9_format.h @@ -255,4 +255,16 @@ namespace dxvk { || format == D3D9Format::DXT5; } + // D3D9 documentation says: IDirect3DSurface9::GetDC is valid on the following formats only: + // D3DFMT_R5G6B5, D3DFMT_X1R5G5B5, D3DFMT_R8G8B8, and D3DFMT_X8R8G8B8. However, + // the equivalent formats of D3DFMT_A1R5G5B5 and D3DFMT_A8R8G8B8 are also supported. + inline bool IsSurfaceGetDCCompatibleFormat(D3D9Format format) { + return format == D3D9Format::R5G6B5 + || format == D3D9Format::X1R5G5B5 + || format == D3D9Format::A1R5G5B5 + || format == D3D9Format::R8G8B8 + || format == D3D9Format::X8R8G8B8 + || format == D3D9Format::A8R8G8B8; + } + } diff --git a/src/d3d9/d3d9_surface.cpp b/src/d3d9/d3d9_surface.cpp index dde921e5b63..2290bfcf6a3 100644 --- a/src/d3d9/d3d9_surface.cpp +++ b/src/d3d9/d3d9_surface.cpp @@ -71,7 +71,7 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE D3D9Surface::QueryInterface(REFIID riid, void** ppvObject) { - if (ppvObject == nullptr) + if (unlikely(ppvObject == nullptr)) return E_POINTER; *ppvObject = nullptr; @@ -101,7 +101,7 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE D3D9Surface::GetDesc(D3DSURFACE_DESC *pDesc) { - if (pDesc == nullptr) + if (unlikely(pDesc == nullptr)) return D3DERR_INVALIDCALL; auto& desc = *(m_texture->Desc()); @@ -190,11 +190,14 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE D3D9Surface::GetDC(HDC *phDC) { - if (phDC == nullptr) + if (unlikely(phDC == nullptr)) return D3DERR_INVALIDCALL; const D3D9_COMMON_TEXTURE_DESC& desc = *m_texture->Desc(); + if (unlikely(!IsSurfaceGetDCCompatibleFormat(desc.Format))) + return D3DERR_INVALIDCALL; + D3DLOCKED_RECT lockedRect; HRESULT hr = LockRect(&lockedRect, nullptr, 0); if (FAILED(hr)) @@ -228,7 +231,7 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE D3D9Surface::ReleaseDC(HDC hDC) { - if (m_dcDesc.hDC == nullptr || m_dcDesc.hDC != hDC) + if (unlikely(m_dcDesc.hDC == nullptr || m_dcDesc.hDC != hDC)) return D3DERR_INVALIDCALL; D3DKMTDestroyDCFromMemory(&m_dcDesc); From 4d624b104ec1e5ff0498110e49d6ef4a9cee969b Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Wed, 27 Nov 2024 11:16:24 +0200 Subject: [PATCH 6/7] [d3d9] Adjust device reset failure error codes --- src/d3d9/d3d9_device.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 772f7c54c3d..548e0d80288 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -522,7 +522,8 @@ namespace dxvk { if (unlikely(m_losableResourceCounter.load() != 0 && !IsExtended() && m_d3d9Options.countLosableResources)) { Logger::warn(str::format("Device reset failed because device still has alive losable resources: Device not reset. Remaining resources: ", m_losableResourceCounter.load())); m_deviceLostState = D3D9DeviceLostState::NotReset; - return D3DERR_DEVICELOST; + // D3D8 expects a D3DERR_DEVICELOST error code + return m_isD3D8Compatible ? D3DERR_DEVICELOST : D3DERR_INVALIDCALL; } hr = ResetSwapChain(pPresentationParameters, nullptr); From aea624d162ad223d82c0991e6c881134d1020a72 Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Wed, 27 Nov 2024 13:14:50 +0200 Subject: [PATCH 7/7] [d3d9] Fixes for state block specific behavior --- src/d3d9/d3d9_device.cpp | 21 ++++++++++++--------- src/d3d9/d3d9_device.h | 12 +++++++----- src/d3d9/d3d9_stateblock.cpp | 9 +++++++-- src/d3d9/d3d9_stateblock.h | 8 +------- 4 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 548e0d80288..0870676680e 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -2599,6 +2599,10 @@ namespace dxvk { IDirect3DStateBlock9** ppSB) { D3D9DeviceLock lock = LockDevice(); + // A state block can not be created while another is being recorded. + if (unlikely(ShouldRecord())) + return D3DERR_INVALIDCALL; + InitReturnPtr(ppSB); if (unlikely(ppSB == nullptr)) @@ -2622,7 +2626,8 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D9DeviceEx::BeginStateBlock() { D3D9DeviceLock lock = LockDevice(); - if (unlikely(m_recorder != nullptr)) + // Only one state block can be recorded at a given time. + if (unlikely(ShouldRecord())) return D3DERR_INVALIDCALL; m_recorder = new D3D9StateBlock(this, D3D9StateBlockType::None); @@ -2634,11 +2639,12 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D9DeviceEx::EndStateBlock(IDirect3DStateBlock9** ppSB) { D3D9DeviceLock lock = LockDevice(); - InitReturnPtr(ppSB); - - if (unlikely(ppSB == nullptr || m_recorder == nullptr)) + // Recording a state block can't end if recording hasn't been started. + if (unlikely(ppSB == nullptr || !ShouldRecord())) return D3DERR_INVALIDCALL; + InitReturnPtr(ppSB); + *ppSB = m_recorder.ref(); if (!m_isD3D8Compatible) m_losableResourceCounter++; @@ -4639,21 +4645,18 @@ namespace dxvk { } - inline bool D3D9DeviceEx::ShouldRecord() { - return m_recorder != nullptr && !m_recorder->IsApplying(); - } - - D3D9_VK_FORMAT_MAPPING D3D9DeviceEx::LookupFormat( D3D9Format Format) const { return m_adapter->GetFormatMapping(Format); } + const DxvkFormatInfo* D3D9DeviceEx::UnsupportedFormatInfo( D3D9Format Format) const { return m_adapter->GetUnsupportedFormatInfo(Format); } + bool D3D9DeviceEx::WaitForResource( const DxvkPagedResource& Resource, uint64_t SequenceNumber, diff --git a/src/d3d9/d3d9_device.h b/src/d3d9/d3d9_device.h index c9c75a8b516..ca7362173df 100644 --- a/src/d3d9/d3d9_device.h +++ b/src/d3d9/d3d9_device.h @@ -1020,6 +1020,13 @@ namespace dxvk { */ void RemoveMappedTexture(D3D9CommonTexture* pTexture); + /** + * \brief Returns whether the device is currently recording a StateBlock + */ + bool ShouldRecord() const { + return m_recorder != nullptr; + } + bool IsD3D8Compatible() const { return m_isD3D8Compatible; } @@ -1160,11 +1167,6 @@ namespace dxvk { */ void WaitStagingBuffer(); - /** - * \brief Returns whether the device is currently recording a StateBlock - */ - inline bool ShouldRecord(); - HRESULT CreateShaderModule( D3D9CommonShader* pShaderModule, uint32_t* pLength, diff --git a/src/d3d9/d3d9_stateblock.cpp b/src/d3d9/d3d9_stateblock.cpp index 85154e209c9..9998fa94d22 100644 --- a/src/d3d9/d3d9_stateblock.cpp +++ b/src/d3d9/d3d9_stateblock.cpp @@ -46,6 +46,10 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D9StateBlock::Capture() { + // A state block can't capture state while another is being recorded. + if (unlikely(m_parent->ShouldRecord())) + return D3DERR_INVALIDCALL; + if (m_captures.flags.test(D3D9CapturedStateFlag::VertexDecl)) SetVertexDeclaration(m_deviceState->vertexDecl.ptr()); @@ -56,13 +60,14 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D9StateBlock::Apply() { - m_applying = true; + // A state block can't be applied while another is being recorded. + if (unlikely(m_parent->ShouldRecord())) + return D3DERR_INVALIDCALL; if (m_captures.flags.test(D3D9CapturedStateFlag::VertexDecl) && m_state.vertexDecl != nullptr) m_parent->SetVertexDeclaration(m_state.vertexDecl.ptr()); ApplyOrCapture(); - m_applying = false; return D3D_OK; } diff --git a/src/d3d9/d3d9_stateblock.h b/src/d3d9/d3d9_stateblock.h index e21cc26df87..74284d4f7ad 100644 --- a/src/d3d9/d3d9_stateblock.h +++ b/src/d3d9/d3d9_stateblock.h @@ -388,10 +388,6 @@ namespace dxvk { HRESULT SetVertexBoolBitfield(uint32_t idx, uint32_t mask, uint32_t bits); HRESULT SetPixelBoolBitfield (uint32_t idx, uint32_t mask, uint32_t bits); - inline bool IsApplying() { - return m_applying; - } - private: void CapturePixelRenderStates(); @@ -407,9 +403,7 @@ namespace dxvk { D3D9CapturableState m_state; D3D9StateCaptures m_captures; - D3D9DeviceState* m_deviceState; - - bool m_applying = false; + D3D9DeviceState* m_deviceState; };